Socket
Socket
Sign inDemoInstall

fs-jetpack

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fs-jetpack - npm Package Compare versions

Comparing version 2.4.0 to 3.0.0

3

CHANGELOG.md

@@ -0,1 +1,4 @@

# 3.0.0 (2020-07-15)
- **(breaking change)** `move()` and `rename()` default overwrite behaviour have changed, now by default both methods throw error if destination path already exists.
# 2.4.0 (2020-05-15)

@@ -2,0 +5,0 @@ - `write()` can accept `mode` as a parameter

24

lib/jetpack.js

@@ -177,9 +177,9 @@ "use strict";

move: (from, to) => {
move.validateInput("move", from, to);
move.sync(resolvePath(from), resolvePath(to));
move: (from, to, options) => {
move.validateInput("move", from, to, options);
move.sync(resolvePath(from), resolvePath(to), options);
},
moveAsync: (from, to) => {
move.validateInput("moveAsync", from, to);
return move.async(resolvePath(from), resolvePath(to));
moveAsync: (from, to, options) => {
move.validateInput("moveAsync", from, to, options);
return move.async(resolvePath(from), resolvePath(to), options);
},

@@ -207,9 +207,9 @@

rename: (path, newName) => {
rename.validateInput("rename", path, newName);
rename.sync(resolvePath(path), newName);
rename: (path, newName, options) => {
rename.validateInput("rename", path, newName, options);
rename.sync(resolvePath(path), newName, options);
},
renameAsync: (path, newName) => {
rename.validateInput("renameAsync", path, newName);
return rename.async(resolvePath(path), newName);
renameAsync: (path, newName, options) => {
rename.validateInput("renameAsync", path, newName, options);
return rename.async(resolvePath(path), newName, options);
},

@@ -216,0 +216,0 @@

@@ -8,9 +8,24 @@ "use strict";

const exists = require("./exists");
const remove = require("./remove");
const validateInput = (methodName, from, to) => {
const methodSignature = `${methodName}(from, to)`;
const validateInput = (methodName, from, to, options) => {
const methodSignature = `${methodName}(from, to, [options])`;
validate.argument(methodSignature, "from", from, ["string"]);
validate.argument(methodSignature, "to", to, ["string"]);
validate.options(methodSignature, "options", options, {
overwrite: ["boolean"]
});
};
const parseOptions = options => {
const opts = options || {};
return opts;
};
const generateDestinationExistsError = path => {
const err = new Error(`Destination path already exists ${path}`);
err.code = "EEXIST";
return err;
};
const generateSourceDoesntExistError = path => {

@@ -26,10 +41,22 @@ const err = new Error(`Path to move doesn't exist ${path}`);

const moveSync = (from, to) => {
const moveSync = (from, to, options) => {
const opts = parseOptions(options);
if (exists.sync(to) !== false && opts.overwrite !== true) {
throw generateDestinationExistsError(to);
}
try {
fs.renameSync(from, to);
} catch (err) {
if (err.code !== "ENOENT") {
// We can't make sense of this error. Rethrow it.
throw err;
} else {
if (
(err.code === "EISDIR" || err.code === "EPERM") &&
opts.overwrite === true
) {
// Looks like the destination path is a directory,
// and we have permission for overwriting, so can remove it.
remove.sync(to);
// Retry the attempt
fs.renameSync(from, to);
} else if (err.code === "ENOENT") {
// Ok, source or destination path doesn't exist.

@@ -46,2 +73,5 @@ // Must do more investigation.

}
} else {
// We can't make sense of this error. Rethrow it.
throw err;
}

@@ -72,30 +102,52 @@ }

const moveAsync = (from, to) => {
const moveAsync = (from, to, options) => {
const opts = parseOptions(options);
return new Promise((resolve, reject) => {
fs.rename(from, to)
.then(resolve)
.catch(err => {
if (err.code !== "ENOENT") {
// Something unknown. Rethrow original error.
reject(err);
} else {
// Ok, source or destination path doesn't exist.
// Must do more investigation.
exists
.async(from)
.then(srcExists => {
if (!srcExists) {
reject(generateSourceDoesntExistError(from));
} else {
ensureDestinationPathExistsAsync(to)
.then(() => {
// Retry the attempt
return fs.rename(from, to);
})
.then(resolve, reject);
}
})
.catch(reject);
}
});
exists.async(to).then(destinationExists => {
if (destinationExists !== false && opts.overwrite !== true) {
reject(generateDestinationExistsError(to));
} else {
fs.rename(from, to)
.then(resolve)
.catch(err => {
if (
(err.code === "EISDIR" || err.code === "EPERM") &&
opts.overwrite === true
) {
// Looks like the destination path is a directory,
// and we have permission for overwriting, so can remove it.
remove
.async(to)
.then(() => {
// Retry the attempt
return fs.rename(from, to);
})
.then(resolve)
.catch(reject);
} else if (err.code === "ENOENT") {
// Ok, source or destination path doesn't exist.
// Must do more investigation.
exists
.async(from)
.then(srcExists => {
if (!srcExists) {
reject(generateSourceDoesntExistError(from));
} else {
ensureDestinationPathExistsAsync(to)
.then(() => {
// Retry the attempt
return fs.rename(from, to);
})
.then(resolve, reject);
}
})
.catch(reject);
} else {
// Something unknown. Rethrow original error.
reject(err);
}
});
}
});
});

@@ -102,0 +154,0 @@ };

@@ -7,6 +7,9 @@ "use strict";

const validateInput = (methodName, path, newName) => {
const methodSignature = `${methodName}(path, newName)`;
const validateInput = (methodName, path, newName, options) => {
const methodSignature = `${methodName}(path, newName, [options])`;
validate.argument(methodSignature, "path", path, ["string"]);
validate.argument(methodSignature, "newName", newName, ["string"]);
validate.options(methodSignature, "options", options, {
overwrite: ["boolean"]
});

@@ -24,5 +27,5 @@ if (pathUtil.basename(newName) !== newName) {

const renameSync = (path, newName) => {
const renameSync = (path, newName, options) => {
const newPath = pathUtil.join(pathUtil.dirname(path), newName);
move.sync(path, newPath);
move.sync(path, newPath, options);
};

@@ -34,5 +37,5 @@

const renameAsync = (path, newName) => {
const renameAsync = (path, newName, options) => {
const newPath = pathUtil.join(pathUtil.dirname(path), newName);
return move.async(path, newPath);
return move.async(path, newPath, options);
};

@@ -39,0 +42,0 @@

{
"name": "fs-jetpack",
"description": "Better file system API",
"version": "2.4.0",
"version": "3.0.0",
"author": "Jakub Szwacz <jakub@szwacz.com>",

@@ -28,5 +28,2 @@ "dependencies": {

},
"engines": {
"node": ">=4"
},
"scripts": {

@@ -33,0 +30,0 @@ "test": "mocha -r ts-node/register \"spec/**/*.spec.ts\"",

@@ -53,3 +53,3 @@ fs-jetpack [![Build Status](https://travis-ci.org/szwacz/fs-jetpack.svg?branch=master)](https://travis-ci.org/szwacz/fs-jetpack) [![Build status](https://ci.appveyor.com/api/projects/status/er206e91fpuuqf58?svg=true)](https://ci.appveyor.com/project/szwacz/fs-jetpack) [![codecov](https://codecov.io/gh/szwacz/fs-jetpack/branch/master/graph/badge.svg)](https://codecov.io/gh/szwacz/fs-jetpack)

If you don't see the word "Async" in method name it returns value immediately.
If you don't see the word "Async" in method name it always, across the whole API returns value immediately.
```js

@@ -409,4 +409,4 @@ const data = jetpack.read('file.txt');

## move(from, to)
asynchronous: **moveAsync(from, to)**
## move(from, to, [options])
asynchronous: **moveAsync(from, to, [options])**

@@ -417,3 +417,5 @@ Moves given path to new location.

`from` path to directory or file you want to move.
`to` path where the thing should be moved.
`to` path where the thing should be moved.
`options` (optional) additional options for customization. Is an `Object` with possible fields:
* `overwrite` (default: `false`) Whether to overwrite destination path when it already exists. If `true`, the overwrite will be performed.

@@ -485,4 +487,4 @@ **returns:**

## rename(path, newName)
asynchronous: **renameAsync(path, newName)**
## rename(path, newName, [options])
asynchronous: **renameAsync(path, newName, [options])**

@@ -493,3 +495,5 @@ Renames given file or directory.

`path` path to thing you want to change name of.
`newName` new name for this thing (not full path, just a name).
`newName` new name for this thing (not full path, just a name).
`options` (optional) additional options for customization. Is an `Object` with possible fields:
* `overwrite` (default: `false`) Whether to overwrite destination path when it already exists. If `true`, the overwrite will be performed.

@@ -496,0 +500,0 @@ **returns:**

@@ -110,2 +110,89 @@ import * as fse from "fs-extra";

describe("overwriting behaviour", () => {
describe("does not overwrite by default", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.outputFileSync("file2.txt", "xyz");
};
const expectations = (err: any) => {
expect(err.code).to.equal("EEXIST");
expect(err.message).to.have.string("Destination path already exists");
path("file2.txt").shouldBeFileWithContent("xyz");
};
it("sync", () => {
preparations();
try {
jetpack.move("file1.txt", "file2.txt");
throw new Error("Expected error to be thrown");
} catch (err) {
expectations(err);
}
});
it("async", done => {
preparations();
jetpack.moveAsync("file1.txt", "file2.txt").catch(err => {
expectations(err);
done();
});
});
});
describe("overwrites if it was specified", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.outputFileSync("file2.txt", "xyz");
};
const expectations = () => {
path("file1.txt").shouldNotExist();
path("file2.txt").shouldBeFileWithContent("abc");
};
it("sync", () => {
preparations();
jetpack.move("file1.txt", "file2.txt", { overwrite: true });
expectations();
});
it("async", done => {
preparations();
jetpack
.moveAsync("file1.txt", "file2.txt", { overwrite: true })
.then(() => {
expectations();
done();
});
});
});
describe("can overwrite a directory", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.mkdirsSync("dir");
};
const expectations = () => {
path("file1.txt").shouldNotExist();
path("dir").shouldBeFileWithContent("abc");
};
it("sync", () => {
preparations();
jetpack.move("file1.txt", "dir", { overwrite: true });
expectations();
});
it("async", done => {
preparations();
jetpack.moveAsync("file1.txt", "dir", { overwrite: true }).then(() => {
expectations();
done();
});
});
});
});
describe("respects internal CWD of jetpack instance", () => {

@@ -156,3 +243,3 @@ const preparations = () => {

test.methodName
}(from, to) must be a string. Received undefined`
}(from, to, [options]) must be a string. Received undefined`
);

@@ -171,3 +258,3 @@ });

test.methodName
}(from, to) must be a string. Received undefined`
}(from, to, [options]) must be a string. Received undefined`
);

@@ -177,3 +264,19 @@ });

});
describe('"options" object', () => {
describe('"overwrite" argument', () => {
tests.forEach(test => {
it(test.type, () => {
expect(() => {
test.method("abc", "xyz", { overwrite: 1 });
}).to.throw(
`Argument "options.overwrite" passed to ${
test.methodName
}(from, to, [options]) must be a boolean. Received number`
);
});
});
});
});
});
});

@@ -62,2 +62,89 @@ import * as pathUtil from "path";

describe("overwriting behaviour", () => {
describe("does not overwrite by default", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.outputFileSync("file2.txt", "xyz");
};
const expectations = (err: any) => {
expect(err.code).to.equal("EEXIST");
expect(err.message).to.have.string("Destination path already exists");
path("file2.txt").shouldBeFileWithContent("xyz");
};
it("sync", () => {
preparations();
try {
jetpack.rename("file1.txt", "file2.txt");
throw new Error("Expected error to be thrown");
} catch (err) {
expectations(err);
}
});
it("async", done => {
preparations();
jetpack.renameAsync("file1.txt", "file2.txt").catch(err => {
expectations(err);
done();
});
});
});
describe("overwrites if it was specified", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.outputFileSync("file2.txt", "xyz");
};
const expectations = () => {
path("file1.txt").shouldNotExist();
path("file2.txt").shouldBeFileWithContent("abc");
};
it("sync", () => {
preparations();
jetpack.rename("file1.txt", "file2.txt", { overwrite: true });
expectations();
});
it("async", done => {
preparations();
jetpack
.renameAsync("file1.txt", "file2.txt", { overwrite: true })
.then(() => {
expectations();
done();
});
});
});
});
describe("can overwrite a directory", () => {
const preparations = () => {
fse.outputFileSync("file1.txt", "abc");
fse.mkdirsSync("dir");
};
const expectations = () => {
path("file1.txt").shouldNotExist();
path("dir").shouldBeFileWithContent("abc");
};
it("sync", () => {
preparations();
jetpack.rename("file1.txt", "dir", { overwrite: true });
expectations();
});
it("async", done => {
preparations();
jetpack.renameAsync("file1.txt", "dir", { overwrite: true }).then(() => {
expectations();
done();
});
});
});
describe("respects internal CWD of jetpack instance", () => {

@@ -108,3 +195,3 @@ const preparations = () => {

test.methodName
}(path, newName) must be a string. Received undefined`
}(path, newName, [options]) must be a string. Received undefined`
);

@@ -124,3 +211,3 @@ });

test.methodName
}(path, newName) must be a string. Received undefined`
}(path, newName, [options]) must be a string. Received undefined`
);

@@ -140,3 +227,3 @@ });

test.methodName
}(path, newName) should be a filename, not a path. Received "${pathToTest}"`
}(path, newName, [options]) should be a filename, not a path. Received "${pathToTest}"`
);

@@ -147,3 +234,19 @@ });

});
describe('"options" object', () => {
describe('"overwrite" argument', () => {
tests.forEach(test => {
it(test.type, () => {
expect(() => {
test.method("abc", "xyz", { overwrite: 1 });
}).to.throw(
`Argument "options.overwrite" passed to ${
test.methodName
}(path, newName, [options]) must be a boolean. Received number`
);
});
});
});
});
});
});

@@ -90,2 +90,10 @@ /// <reference types="node" />

type MoveOptions = {
overwrite?: boolean;
};
type RenameOptions = {
overwrite?: boolean;
};
// API has the same set of synchronous and asynchronous methods.

@@ -310,4 +318,5 @@ // All async methods are promise based (no callbacks).

* @param to path
* @param options
*/
move(from: string, to: string): void;
move(from: string, to: string, options?: MoveOptions): void;

@@ -319,4 +328,5 @@ /**

* @param to path
* @param options
*/
moveAsync(from: string, to: string): Promise<void>;
moveAsync(from: string, to: string, options?: MoveOptions): Promise<void>;

@@ -367,4 +377,5 @@ /**

* @param newName just the name of the thing being renamed
* @param options
*/
rename(path: string, newName: string): void;
rename(path: string, newName: string, options?: RenameOptions): void;
/**

@@ -375,4 +386,9 @@ * Renames given file or directory.

* @param newName just the name of the thing being renamed
* @param options
*/
renameAsync(path: string, newName: string): Promise<void>;
renameAsync(
path: string,
newName: string,
options?: RenameOptions
): Promise<void>;

@@ -379,0 +395,0 @@ /**

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc