Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add appveyor config #57

Merged
merged 5 commits into from
Dec 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
environment:
matrix:
- nodejs_version: "8"
- nodejs_version: "10"
- nodejs_version: "11"

install:
- ps: Install-Product node $env:nodejs_version
- yarn

test_script:
- yarn run test

build: off

cache:
- node_modules
- "%LOCALAPPDATA%\\Yarn"
67 changes: 29 additions & 38 deletions src/nextra/move.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
const { resolve, dirname } = require('path');

const { setTimeoutPromise, moveAcrossDevice } = require('../util');
const { access, rename, link, unlink } = require('../fs');
const { isSrcKid } = require('../util');
const { access, rename, stat } = require('../fs');

const remove = require('./remove');
const mkdirs = require('./mkdirs');
const pathExists = require('./pathExists');
const copy = require('./copy');

/**
* @typedef {Object} MoveOptions
* @memberof fsn/nextra
* @property {boolean} [mkdirp = true] Should the move create directories recursively for the destination path
* @property {boolean} [overwrite = false] Should the move overwrite an identical file at the destination path
* @property {boolean} [clobber = false] Alias to overwrite for parity to fs-extra
*/

/**
Expand All @@ -23,43 +23,34 @@ const mkdirs = require('./mkdirs');
* @returns {Promise<void>}
*/
module.exports = async function move(source, destination, options = {}) {
const shouldMkdirp = 'mkdirp' in options ? options.mkdirp : true;
const overwrite = options.overwrite || options.clobber || false;
const overwrite = options.overwrite || false;
if (resolve(source) === resolve(destination)) return access(source);

if (shouldMkdirp) await mkdirs(dirname(destination));

if (resolve(source) === resolve(destination)) {
return access(source);
} else if (overwrite) {
return rename(source, destination)
.catch(async (err) => {
if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
await remove(destination);
options.overwrite = false;
return move(source, destination, options);
}
const myStat = await stat(source);
if (myStat.isDirectory() && isSrcKid(source, destination)) {
throw new Error('FS-NEXTRA: Moving a parent directory into a child will result in an infinite loop.');
}

// Windows
/* istanbul ignore if */
if (err.code === 'EPERM') {
await setTimeoutPromise(200);
await remove(destination);
options.overwrite = false;
return move(source, destination, options);
}
await mkdirs(dirname(destination));

// For renaming files across devices, can't test via travis
/* istanbul ignore if */
if (err.code === 'EXDEV') return moveAcrossDevice(source, destination, overwrite);
if (overwrite) {
await remove(destination);
} else if (await pathExists(destination)) {
throw new Error('FS-NEXTRA: Destination already exists.');
}

// Any other error
throw err;
});
try {
return await rename(source, destination);
} catch (err) {
if (err.code === 'EXDEV') {
const opts = {
overwrite,
errorOnExist: true
};

await copy(source, destination, opts);
return remove(source);
}
throw err;
}
return link(source, destination)
.then(() => unlink(source))
.catch(err => {
if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') return moveAcrossDevice(source, destination, overwrite);
throw err;
});
};
48 changes: 1 addition & 47 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { promisify } = require('util');
const { randomBytes } = require('crypto');
const { tmpdir } = require('os');

const { rmdir, lstat, createReadStream, createWriteStream, unlink, stat, chmod, readdir, readlink, mkdir, symlink, copyFile } = require('./fs');
const { rmdir, lstat, unlink, stat, chmod, readdir, readlink, mkdir, symlink, copyFile } = require('./fs');

const remove = require('./nextra/remove');
const pathExists = require('./nextra/pathExists');
Expand Down Expand Up @@ -49,52 +49,6 @@ exports.symlinkPaths = async (srcpath, dstpath) => {
return { toCwd: srcpath, toDst: relative(dstdir, srcpath) };
};

// Impossible to test on travis
/* istanbul ignore next */
exports.moveAcrossDevice = async (source, dest, overwrite) => {
const stats = await stat(source);
if (stats.isDirectory()) return this.moveDirAcrossDevice(source, dest, overwrite);
return this.moveFileAcrossDevice(source, dest, overwrite);
};

// Impossible to test on travis
/* istanbul ignore next */
exports.moveFileAcrossDevice = (source, dest, overwrite) => new Promise((res, rej) => {
const flags = overwrite ? 'w' : 'wx';
const ins = createReadStream(source);
const outs = createWriteStream(dest, { flags });

ins.on('error', err => {
ins.destroy();
outs.destroy();
outs.removeListener('close', () => { res(unlink(source)); });

unlink(dest).catch(() => {
if (err.code !== 'EISDIR' && err.code !== 'EPERM') rej(err);
res(this.moveDirAcrossDevice(source, dest, overwrite).catch(rej));
});
});

outs.on('error', err => {
ins.destroy();
outs.destroy();
outs.removeListener('close', () => { res(unlink(source)); });
rej(err);
});

outs.once('close', () => { res(unlink(source)); });
ins.pipe(outs);
});

// Impossible to test on travis
/* istanbul ignore next */
exports.moveDirAcrossDevice = async (source, dest, overwrite) => {
const options = { overwrite: false };
if (overwrite) await remove(dest);
await this.ncp(source, dest, options);
return remove(source);
};

exports.rimraf = async (myPath, options) => {
const stats = await lstat(myPath).catch(er => {
// Windows
Expand Down
38 changes: 18 additions & 20 deletions test/move.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const ava = require('ava');
const { fs, tempFileLoc, tempDirLoc, tempFile, tempDir } = require('./lib');
const nextra = require('../src');

// Good Usage (should get desired results)

ava('standard usage', async test => {
test.plan(2);
const existing = tempFile();
Expand All @@ -17,12 +19,6 @@ ava('self', async test => {
test.deepEqual(await nextra.move(existing, existing, { overwrite: true }), await fs.accessAsync(existing));
});

ava('overwrite dir with file', async test => {
const existing = tempFile();
const move = tempDir();
await test.throwsAsync(nextra.move(existing, move, { overwrite: true }));
});

ava('overwrite existing file', async test => {
test.plan(2);
const existing = tempFile();
Expand All @@ -43,28 +39,16 @@ ava('no overwrite non-existent file', async test => {
await test.throwsAsync(fs.accessAsync(existing));
});

ava('no overwrite existing file', async test => {
const existing = tempFile();
const move = tempFile();
await test.throwsAsync(nextra.move(existing, move, { overwrite: false }));
});

ava('deep mkdirp', async test => {
ava('deep destination', async test => {
test.plan(2);
const existing = tempFile();
const move = tempFileLoc(tempDirLoc());
await nextra.move(existing, move, { mkdirp: true });
await nextra.move(existing, move);

await test.notThrowsAsync(fs.accessAsync(move));
await test.throwsAsync(fs.accessAsync(existing));
});

ava('deep no mkdirp', async test => {
const existing = tempFile();
const move = tempFileLoc(tempDirLoc());
await test.throwsAsync(nextra.move(existing, move, { mkdirp: false }));
});

ava('overwrite full directory', async test => {
test.plan(2);
const existing = tempDir();
Expand All @@ -77,6 +61,20 @@ ava('overwrite full directory', async test => {
await test.throwsAsync(fs.accessAsync(existing));
});

// Bad Usage (function should throw)

ava('Directory to Child Directory', async test => {
const parent = tempDir();
const child = tempDir(parent);
await test.throwsAsync(nextra.move(parent, child));
});

ava('no overwrite existing file', async test => {
const existing = tempFile();
const move = tempFile();
await test.throwsAsync(nextra.move(existing, move, { overwrite: false }));
});

ava('no overwrite full directory', async test => {
const existing = tempDir();
tempFile(existing);
Expand Down