UNPKG

20.5 kBMarkdownView Raw
1fs-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)
2==========
3
4Node's [fs library](http://nodejs.org/api/fs.html) is very low level and because of that often painful to use. *fs-jetpack* wants to fix that by giving you completely rethought, much more convenient API to work with file system.
5
6Check out [EXAMPLES](EXAMPLES.md) to see few snippets what it can do.
7
8## Installation
9```
10npm install fs-jetpack
11```
12
13## Usage
14```javascript
15var jetpack = require('fs-jetpack');
16```
17
18# Sync & Async
19
20API has the same set of synchronous and asynchronous methods. All async methods are promise based (no callbacks).
21
22Commonly used naming convention in Node world is reversed in this library (no 'method' and 'methodSync' naming). Asynchronous methods are those with 'Async' suffix, all methods without 'Async' in the name are synchronous. Reason behind this is that it gives very nice look to blocking API. And promise-based, non-blocking code is verbose anyway, so one more word is not much of a difference.
23
24Also it's just convenient...
25
26If you don't see the word "Async" in method name it returns value immediately.
27```js
28var data = jetpack.read('file.txt');
29console.log(data);
30```
31
32When you see "Async" that method returns promise which when resolved returns value.
33```js
34jetpack.readAsync('file.txt')
35.then(function (data) {
36 console.log(data);
37});
38```
39
40# API
41
42- [append](#appendpath-data-options)
43- [copy](#copyfrom-to-options)
44- [createReadStream](#createreadstreampath-options)
45- [createWriteStream](#createwritestreampath-options)
46- [cwd](#cwdpath)
47- [dir](#dirpath-criteria)
48- [exists](#existspath)
49- [file](#filepath-criteria)
50- [find](#findpath-searchoptions)
51- [inspect](#inspectpath-options)
52- [inspectTree](#inspecttreepath-options)
53- [list](#listpath)
54- [move](#movefrom-to)
55- [path](#pathparts)
56- [read](#readpath-returnas)
57- [remove](#removepath)
58- [rename](#renamepath-newname)
59- [symlink](#symlinksymlinkvalue-path)
60- [write](#writepath-data-options)
61
62
63## append(path, data, [options])
64asynchronous: **appendAsync(path, data, [options])**
65
66Appends given data to the end of file. If file or any parent directory doesn't exist it will be created.
67
68**arguments:**
69`path` the path to file.
70`data` data to append (can be `String` or `Buffer`).
71`options` (optional) `Object` with possible fields:
72* `mode` if the file doesn't exist yet, will be created with given mode. Value could be number (eg. `0700`) or string (eg. `'700'`).
73
74**returns:**
75Nothing.
76
77
78## copy(from, to, [options])
79asynchronous: **copyAsync(from, to, [options])**
80
81Copies given file or directory (with everything inside).
82
83**arguments:**
84`from` path to location you want to copy.
85`to` path to destination location, where the copy should be placed.
86`options` (optional) additional options for customization. Is an `Object` with possible fields:
87* `overwrite` (default: `false`) Whether to overwrite destination path if arready exists. For directories, source directory is merged with destination directory, so files in destination which are not present in the source, will remain intact.
88* `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](#matching-patterns)).
89
90**returns:**
91Nothing.
92
93**examples:**
94```javascript
95// Copies a file (and replaces it if one already exists in 'foo' directory)
96jetpack.copy('file.txt', 'foo/file.txt', { overwrite: true });
97
98// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'.
99jetpack.copy('foo', 'bar', { matching: '*.md' });
100// Copies only '.md' and '.txt' files from 'foo' (and subdirectories of 'foo') to 'bar'.
101jetpack.copy('foo', 'bar', { matching: ['*.md', '*.txt'] });
102
103// You can filter previous matches by defining negated pattern further in the order:
104// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'
105// but will skip file '!top-secret.md'.
106jetpack.copy('foo', 'bar', { matching: ['*.md', '!top-secret.md'] });
107// Copies only '.md' files from 'foo' (and subdirectories of 'foo') to 'bar'
108// but will skip all files in 'foo/top-secret' directory.
109jetpack.copy('foo', 'bar', { matching: ['*.md', '!top-secret/**/*'] });
110
111// All patterns are anchored to directory you want to copy, not to CWD.
112// So in this example directory 'dir1/dir2/images' will be copied
113// to 'copied-dir2/images'
114jetpack.copy('dir1/dir2', 'copied-dir2', {
115 matching: 'images/**'
116});
117```
118
119
120## createReadStream(path, [options])
121
122Just an alias to vanilla [fs.createReadStream](http://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options).
123
124
125## createWriteStream(path, [options])
126
127Just an alias to vanilla [fs.createWriteStream](http://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options).
128
129
130
131## cwd([path...])
132Returns Current Working Directory (CWD) for this instance of jetpack, or creates new jetpack object with given path as its internal CWD.
133
134**Note:** fs-jetpack never changes value of `process.cwd()`, the CWD we are talking about here is internal value inside every jetpack instance.
135
136**arguments:**
137`path...` (optional) path (or many path parts) to become new CWD. Could be absolute, or relative. If relative path given new CWD will be resolved basing on current CWD of this jetpack instance.
138
139**returns:**
140If `path` not specified, returns CWD path of this jetpack object. For main instance of fs-jetpack it is always `process.cwd()`.
141If `path` specified, returns new jetpack object (totally the same thing as main jetpack). The new object resolves paths according to its internal CWD, not the global one (`process.cwd()`).
142
143**examples:**
144```javascript
145// Let's assume that process.cwd() outputs...
146console.log(process.cwd()); // '/one/two/three'
147// jetpack.cwd() will always return the same value as process.cwd()
148console.log(jetpack.cwd()); // '/one/two/three'
149
150// Now let's create new CWD context...
151var jetParent = jetpack.cwd('..');
152console.log(jetParent.cwd()); // '/one/two'
153// ...and use this new context.
154jetParent.dir('four'); // we just created directory '/one/two/four'
155
156// One CWD context can be used to create next CWD context.
157var jetParentParent = jetParent.cwd('..');
158console.log(jetParentParent.cwd()); // '/one'
159
160// When many parameters specified they are treated as parts of path to resolve
161var sillyCwd = jetpack.cwd('a', 'b', 'c');
162console.log(sillyCwd.cwd()); // '/one/two/three/a/b/c'
163```
164
165
166## dir(path, [criteria])
167asynchronous: **dirAsync(path, [criteria])**
168
169Ensures that directory on given path exists and meets given criteria. If any criterium is not met it will be after this call. If any parent directory in `path` doesn't exist it will be created (like `mkdir -p`).
170
171**arguments:**
172`path` path to directory to examine.
173`criteria` (optional) criteria to be met by the directory. Is an `Object` with possible fields:
174* `empty` (default: `false`) whether directory should be empty (no other files or directories inside). If set to `true` and directory contains any files or subdirectories all of them will be deleted.
175* `mode` ensures directory has specified mode. If not set and directory already exists, current mode will be preserved. Value could be number (eg. `0700`) or string (eg. `'700'`).
176
177**returns:**
178New CWD context with directory specified in `path` as CWD (see docs of `cwd()` method for explanation).
179
180**examples:**
181```javascript
182// Creates directory if doesn't exist
183jetpack.dir('new-dir');
184
185// Makes sure directory mode is 0700 and that it's empty
186jetpack.dir('empty-dir', { empty: true, mode: '700' });
187
188// Because dir returns new CWD context pointing to just
189// created directory you can create dir chains.
190jetpack
191.dir('main-dir') // creates 'main-dir'
192.dir('sub-dir'); // creates 'main-dir/sub-dir'
193```
194
195
196## exists(path)
197asynchronous: **existsAsync(path)**
198
199Checks whether something exists on given `path`. This method returns values more specific than `true/false` to protect from errors like "I was expecting directory, but it was a file".
200
201**returns:**
202* `false` if path doesn't exist.
203* `"dir"` if path is a directory.
204* `"file"` if path is a file.
205* `"other"` if none of the above.
206
207
208## file(path, [criteria])
209asynchronous: **fileAsync(path, [criteria])**
210
211Ensures that file exists and meets given criteria. If any criterium is not met it will be after this call. If any parent directory in `path` doesn't exist it will be created (like `mkdir -p`).
212
213**arguments:**
214`path` path to file to examine.
215`criteria` (optional) criteria to be met by the file. Is an `Object` with possible fields:
216* `content` sets file content. Can be `String`, `Buffer`, `Object` or `Array`. If `Object` or `Array` given to this parameter data will be written as JSON.
217* `jsonIndent` (defaults to 2) if writing JSON data this tells how many spaces should one indentation have.
218* `mode` ensures file has specified mode. If not set and file already exists, current mode will be preserved. Value could be number (eg. `0700`) or string (eg. `'700'`).
219
220**returns:**
221Jetpack object you called this method on (self).
222
223**examples:**
224```javascript
225// Creates file if doesn't exist
226jetpack.file('something.txt');
227
228// Creates file with mode '777' and content 'Hello World!'
229jetpack.file('hello.txt', { mode: '777', content: 'Hello World!' });
230```
231
232
233## find([path], searchOptions)
234asynchronous: **findAsync([path], searchOptions)**
235
236Finds in directory specified by `path` all files fulfilling `searchOptions`. Returned paths are relative to current CWD of jetpack instance.
237
238**arguments:**
239`path` (optional, defaults to `'.'`) path to start search in (all subdirectories will be searched).
240`searchOptions` is an `Object` with possible fields:
241* `matching` glob patterns of files you want to find ([all possible globs are described further in this readme](#matching-patterns)).
242* `files` (default `true`) whether or not should search for files.
243* `directories` (default `false`) whether or not should search for directories.
244* `recursive` (default `true`) whether the whole directory tree should be searched recursively, or only one-level of given directory (excluding it's subdirectories).
245
246**returns:**
247`Array` of found paths.
248
249**examples:**
250```javascript
251// Finds all files which has 2015 in the name
252jetpack.find('my-work', { matching: '*2015*' });
253
254// Finds all '.txt' files inside 'foo/bar' directory and its subdirectories
255jetpack.find('foo', { matching: 'bar/**/*.txt' });
256// Finds all '.txt' files inside 'foo/bar' directory WITHOUT subdirectories
257jetpack.find('foo', { matching: 'bar/*.txt' });
258
259// Finds all '.js' files inside 'my-project' but excluding those in 'vendor' subtree.
260jetpack.find('my-project', { matching: ['*.js', '!vendor/**/*'] });
261
262// Looks for all directories named 'foo' (and will omit all files named 'foo').
263jetpack.find('my-work', { matching: ['foo'], files: false, directories: true });
264
265// Finds all '.txt' files inside 'foo' directory WITHOUT subdirectories
266jetpack.find('foo', { matching: './*.txt' });
267// This line does the same as the above, but has better performance
268// (skips looking in subdirectories)
269jetpack.find('foo', { matching: '*.txt', recursive: false });
270
271// Path parameter might be omitted and CWD is used as path in that case.
272var myStuffDir = jetpack.cwd('my-stuff');
273myStuffDir.find({ matching: ['*.md'] });
274```
275
276## inspect(path, [options])
277asynchronous: **inspectAsync(path, [options])**
278
279Inspects given path (replacement for `fs.stat`). Returned object by default contains only very basic, not platform-dependent properties (so you have something e.g. your unit tests can rely on), you can enable more properties through options object.
280
281**arguments:**
282`path` path to inspect.
283`options` (optional). Possible values:
284* `checksum` if specified will return checksum of inspected file. Possible values are strings `'md5'`, `'sha1'`, `'sha256'` or `'sha512'`. If given path is directory this field is ignored.
285* `mode` (default `false`) if set to `true` will add file mode (unix file permissions) value.
286* `times` (default `false`) if set to `true` will add atime, mtime and ctime fields (here called `accessTime`, `modifyTime` and `changeTime`).
287* `absolutePath` (default `false`) if set to `true` will add absolute path to this resource.
288* `symlinks` (default `'report'`) if a given path is a symlink by default `inspect` will report that symlink (not follow it). You can flip this behaviour by setting this option to `'follow'`.
289
290**returns:**
291`undefined` if given path doens't exist.
292Otherwise `Object` of structure:
293```javascript
294{
295 name: "my_dir",
296 type: "file", // possible values: "file", "dir", "symlink"
297 size: 123, // size in bytes, this is returned only for files
298 // if checksum option was specified:
299 md5: '900150983cd24fb0d6963f7d28e17f72',
300 // if mode option was set to true:
301 mode: 33204,
302 // if times option was set to true:
303 accessTime: [object Date],
304 modifyTime: [object Date],
305 changeTime: [object Date]
306}
307```
308
309
310## inspectTree(path, [options])
311asynchronous: **inspectTreeAsync(path, [options])**
312
313Calls [inspect](#inspect) recursively on given path so it creates tree of all directories and sub-directories inside it.
314
315**arguments:**
316`path` the starting path to inspect.
317`options` (optional). Possible values:
318* `checksum` if specified will also calculate checksum of every item in the tree. Possible values are strings `'md5'`, `'sha1'`, `'sha256'` or `'sha512'`. Checksums for directories are calculated as checksum of all children' checksums plus their filenames (see example below).
319* `relativePath` if set to `true` every tree node will have relative path anchored to root inspected folder.
320* `symlinks` (default `'report'`) if a given path is a symlink by default `inspectTree` will report that symlink (not follow it). You can flip this behaviour by setting this option to `'follow'`.
321
322**returns:**
323`undefined` if given path doesn't exist.
324Otherwise tree of inspect objects like:
325```javascript
326{
327 name: 'my_dir',
328 type: 'dir',
329 size: 123, // this is combined size of all items in this directory
330 relativePath: '.',
331 md5: '11c68d9ad988ff4d98768193ab66a646',
332 // checksum of this directory was calculated as:
333 // md5(child[0].name + child[0].md5 + child[1].name + child[1].md5)
334 children: [
335 {
336 name: 'empty',
337 type: 'dir',
338 size: 0,
339 relativePath: './dir',
340 md5: 'd41d8cd98f00b204e9800998ecf8427e',
341 children: []
342 },{
343 name: 'file.txt',
344 type: 'file',
345 size: 123,
346 relativePath: './file.txt',
347 md5: '900150983cd24fb0d6963f7d28e17f72'
348 }
349 ]
350}
351```
352
353
354## list([path])
355asynchronous: **listAsync(path)**
356
357Lists the contents of directory. Equivalent of `fs.readdir`.
358
359**arguments:**
360`path` (optional) path to directory you would like to list. If not specified defaults to CWD.
361
362**returns:**
363Array of file names inside given path, or `undefined` if given path doesn't exist.
364
365
366## move(from, to)
367asynchronous: **moveAsync(from, to)**
368
369Moves given path to new location.
370
371**arguments:**
372`from` path to directory or file you want to move.
373`to` path where the thing should be moved.
374
375**returns:**
376Nothing.
377
378
379## path(parts...)
380Returns path resolved to internal CWD of this jetpack object.
381
382**arguments:**
383`parts` strings to join and resolve as path (as many as you like).
384
385**returns:**
386Resolved path as string.
387
388**examples:**
389```javascript
390jetpack.cwd(); // if it returns '/one/two'
391jetpack.path(); // this will return the same '/one/two'
392jetpack.path('three'); // this will return '/one/two/three'
393jetpack.path('..', 'four'); // this will return '/one/four'
394```
395
396
397## read(path, [returnAs])
398asynchronous: **readAsync(path, [returnAs])**
399
400Reads content of file.
401
402**arguments:**
403`path` path to file.
404`returnAs` (optional) how the content of file should be returned. Is a string with possible values:
405* `'utf8'` (default) content will be returned as UTF-8 String.
406* `'buffer'` content will be returned as a Buffer.
407* `'json'` content will be returned as parsed JSON object.
408* `'jsonWithDates'` content will be returned as parsed JSON object, and date strings in [ISO format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) will be automatically turned into Date objects.
409
410**returns:**
411File content in specified format, or `undefined` if file doesn't exist.
412
413
414## remove([path])
415asynchronous: **removeAsync([path])**
416
417Deletes given path, no matter what it is (file, directory or non-empty directory). If path already doesn't exist terminates gracefully without throwing, so you can use it as 'ensure path doesn't exist'.
418
419**arguments:**
420`path` (optional) path to file or directory you want to remove. If not specified the remove action will be performed on current working directory (CWD).
421
422**returns:**
423Nothing.
424
425**examples:**
426```javascript
427// Deletes file
428jetpack.remove('my_work/notes.txt');
429
430// Deletes directory "important_stuff" and everything inside
431jetpack.remove('my_work/important_stuff');
432
433// Remove can be called with no parameters and will default to CWD then.
434// In this example folder 'my_work' will cease to exist.
435var myStuffDir = jetpack.cwd('my_stuff');
436myStuffDir.remove();
437```
438
439
440## rename(path, newName)
441asynchronous: **renameAsync(path, newName)**
442
443Renames given file or directory.
444
445**arguments:**
446`path` path to thing you want to change name of.
447`newName` new name for this thing (not full path, just a name).
448
449**returns:**
450Nothing.
451
452**examples:**
453```javascript
454// The file "my_work/important.md" will be renamed to "my_work/very_important.md"
455jetpack.rename('my_work/important.txt', 'very_important.md');
456```
457
458## symlink(symlinkValue, path)
459asynchronous: **symlinkAsync(symlinkValue, path)**
460
461Creates symbolic link.
462
463**arguments:**
464`symlinkValue` path where symbolic link should point.
465`path` path where symbolic link should be put.
466
467**returns:**
468Nothing.
469
470
471## write(path, data, [options])
472asynchronous: **writeAsync(path, data, [options])**
473
474Writes data to file. If any parent directory in `path` doesn't exist it will be created (like `mkdir -p`).
475
476**arguments:**
477`path` path to file.
478`data` data to be written. This could be `String`, `Buffer`, `Object` or `Array` (if last two used, the data will be outputted into file as JSON).
479`options` (optional) `Object` with possible fields:
480* `atomic` (default `false`) if set to `true` the file will be written using strategy which is much more resistant to data loss. The trick is very simple, [read this to get the concept](http://stackoverflow.com/questions/17047994/transactionally-writing-files-in-node-js).
481* `jsonIndent` (defaults to 2) if writing JSON data this tells how many spaces should one indentation have.
482
483**returns:**
484Nothing.
485
486# Matching patterns
487
488API methods [copy](#copyfrom-to-options) and [find](#findpath-searchoptions) have `matching` option. Those are all the possible tokens to use there:
489
490- `*` - Matches 0 or more characters in a single path portion.
491- `?` - Matches 1 character.
492- `!` - Used as the first character in pattern will invert the matching logic (match everything what **is not** matched by tokens further in this pattern).
493- `[...]` - Matches a range of characters, similar to a RegExp range. If the first character of the range is `!` or `^` then it matches any character not in the range.
494- `@(pattern|pat*|pat?ern)` - Matches exactly one of the patterns provided.
495- `+(pattern|pat*|pat?ern)` - Matches one or more occurrences of the patterns provided.
496- `?(pattern|pat*|pat?ern)` - Matches zero or one occurrence of the patterns provided.
497- `*(pattern|pat*|pat?ern)` - Matches zero or more occurrences of the patterns provided.
498- `!(pattern|pat*|pat?ern)` - Matches anything that does not match any of the patterns provided.
499- `**` - If a "globstar" is alone in a path portion, then it matches zero or more directories and subdirectories.
500
501*(explanation borrowed from [glob](https://github.com/isaacs/node-glob) which is using [the same matching library](https://github.com/isaacs/minimatch) as this project)*
502
503# License
504
505Released under the MIT license.