API for your everyday file system manipulations, much more convenient than fs or fs-extra. You will especially appreciate it as a scripting/tooling library and for your build pipelines.
API:
append
copy
createReadStream
createWriteStream
cwd
dir
exists
file
find
inspect
inspectTree
list
move
path
read
remove
rename
symlink
tmpDir
write
You can create many fs-jetpack objects with different internal working directories (which are independent from process.cwd()
) and work on directories in a little more object-oriented manner.
const src = jetpack.cwd("path/to/source");
const dest = jetpack.cwd("/some/different/path/to/destination");
src.copy("foo.txt", dest.path("bar.txt"));
You can write JavaScript object directly to disk and it will be transformed into JSON automatically.
const obj = { greet: "Hello World!" };
jetpack.write("file.json", obj);
Then you can get your object back just by telling read
method that it's a JSON.
const obj = jetpack.read("file.json", "json");
Everyone who has a lot to do with file system probably is sick of seeing error "ENOENT, no such file or directory". Fs-jetpack tries to recover from this.
- For write/creation operations, if any of parent directories doesn't exist jetpack will just create them as well (like
mkdir -p
works). - For read/inspect operations, if file or directory doesn't exi
8000
st
undefined
is returned instead of throwing.
API has the same set of synchronous and asynchronous methods. All async methods are promise based (no callbacks).
Commonly used naming convention in node.js world has been flipped in this API, so no method()
(async) and methodSync()
naming. Here the convention is methodAsync()
and method()
(sync). I know this looks wrong to you, but bear with me. Thanks to that, you always know how fs-jetpack method behaves, just by looking at the name: If you don't see the word "Async", this method returns value immediately, if you do, promise is returned. Standard node.js naming can't give you this clarity.
// Synchronous call
const data = jetpack.read('file.txt');
console.log(data);
// Asynchronous call
const data = await jetpack.readAsync('file.txt');
console.log(data);
Let's say you want to create folder structure as demonstrated in comment below. Piece of cake!
// .
// |- greets
// |- greet.txt
// |- greet.json
// |- greets-i18n
// |- polish.txt
jetpack
.dir("greets")
.file("greet.txt", { content: "Hello world!" })
.file("greet.json", { content: { greet: "Hello world!" } })
.cwd("..")
.dir("greets-i18n")
.file("polish.txt", { content: "Witaj świecie!" });
Need to copy whole directory of files, but first perform some transformations on each file?
const src = jetpack.cwd("path/to/source/folder");
const dst = jetpack.cwd("path/to/destination");
src.find({ matching: "*" }).forEach((path) => {
const content = src.read(path);
const transformedContent = transformTheFileHoweverYouWant(content);
dst.write(path, transformedContent);
});
Need to delete all temporary and log files inside my_folder
tree?
jetpack.find("my_folder", { matching: ["*.tmp", "*.log"] }).forEach(jetpack.remove);
Need to perform temporary data transformations?
const dir = jetpack.tmpDir();
dir.write("data.txt", myData);
// Perform some operations on the data and when you're done
// and don't need the folder any longer just call...
dir.remove();
npm install fs-jetpack
Import to your code:
const jetpack = require("fs-jetpack");
Starting from v2.1.0 fs-jetpack is TypeScript compatible. But for backwards compatibility purposes all types and interfaces are reachable through special path fs-jetpack/types
.
// Import fs-jetpack into TypeScript code (the jetpack typings will be loaded as well).
import * as jetpack from "fs-jetpack";
// Import one of jetpack's interfaces to cast it on a variable declaration.
import { InspectResult } from "fs-jetpack/types";
let result: InspectResult = jetpack.inspect("foo");
This API is considered stable and all breaking changes to it are done as completely last resort. It also uses "better safe than sorry" approach to bumping major version number. So in 99.9% of cases you can upgrade to latest version with no worries, because all major version bumps so far, were due to edge case behaviour changes.
asynchronous: appendAsync(path, data, [options])
Appends given data to the end of file. If file or any parent directory doesn't exist it will be created.
arguments:
path
the path to file.
data
data to append (can be String
or Buffer
).
options
(optional) Object
with possible fields:
mode
if the file doesn't exist yet, will be created with given mode. Value could be number (eg.0o700
) or string (eg.'700'
).
returns:
Nothing.
asynchronous: copyAsync(from, to, [options])
Copies given file or directory (with everything inside).
arguments:
from
path to location you want to copy.
to
path to destination location, where the copy should be placed.
options
(optional) additional options for customization. Is an Object
with possible fields:
overwrite
(default:false
) Whether to overwrite destination path when it already exists. Can beBoolean
orFunction
. Iffalse
, an error will be thrown if it already exists. Iftrue
, the overwrite will be performed (for directories, this overwrite consists of a recursive merge - i.e. only files that already exist in the destination directory will be overwritten). If a function was provided, every time there is a file conflict while copying the function will be invoked with inspect objects of both: source and destination file and overwrites the file only iftrue
has been returned from the function (see example below). In async mode, the overwrite function can also return a promise, so you can perform multi step processes to determine if file should be overwritten or not (see example below).matching
if defined will actually copy only items matching any of specified glob patterns and omit everything else (all possible globs are described further in this readme).ignoreCase
(defaultfalse
) whether or not case should be ignored when processing glob patterns passed through thematching
option.
returns:
Nothing.
examples:
// Copies a file (and replaces it if one already exists in 'foo' directory)
jetpack.copy("file.txt", "foo/file.txt", { overwrite: true });
// Copies files from folder foo_1 to foo_final, but overwrites in
// foo_final only files which are newer in foo_1.
jetpack.copy("foo_1", "foo_final", {
overwrite: (srcInspectData, destInspectData) => {
return srcInspectData.modifyTime > destInspectData.modifyTime;
}
});
// Asynchronously copies files from folder foo_1 to foo_final,
// but overwrites only files containing "John Doe" string.
jetpack.copyAsync("foo_1", "foo_final", {
overwrite: (srcInspectData, destInspectData) => {
return jetpack.readAsync(srcInspectData.absolutePath).then(data => {
return data.includes("John Doe");
});
}
});
// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'.
jetpack.copy("foo", "bar", { matching: "*.md" });
// Copies only '.md' and '.txt' files from 'foo' (and subdirectories of 'foo') to 'bar'.
jetpack.copy("foo", "bar", { matching: ["*.md", "*.txt"] });
// You can filter previous matches by defining negated pattern further in the order:
// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'
// but will skip file '!top-secret.md'.
jetpack.copy("foo", "bar", { matching: ["*.md", "!top-secret.md"] });
// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'
// but will skip all files in 'foo/top-secret' directory.
jetpack.copy("foo", "bar", { matching: ["*.md", "!top-secret/**/*"] });
// All patterns are anchored to directory you want to copy, not to CWD.
// So in this example directory 'dir1/dir2/images' will be copied
// to 'copied-dir2/images'
jetpack.copy("dir1/dir2", "copied-dir2", {
matching: "images/**"
});