Adding support for promises. Closes #379, #382 (#380)

* adding promise-based filedescriptor

* fixing promisify dep

* promisifying shell

* deprecating 'exists' method on fs

* adding docs

* fixing docs

* removing redundant code.
This commit is contained in:
Dmytro 2018-08-27 19:50:41 -04:00 committed by David Humphrey
parent de45918cbc
commit 353290a08f
7 changed files with 1889 additions and 1840 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ bower_components
.env
*~
dist/filer-issue225.js
.vscode
# Parcel build dirs
.cache

View File

@ -80,6 +80,7 @@ instance. By default, a new [IndexedDB](https://developer.mozilla.org/en/docs/I
database is created for each file system. The file system can also use other
backend storage providers, for example [WebSQL](http://en.wikipedia.org/wiki/Web_SQL_Database)
or even RAM (i.e., for temporary storage). See the section on [Storage Providers](#providers).
<a name="overviewExample"></a>
```javascript
var fs = new Filer.FileSystem();
@ -110,6 +111,21 @@ Like node.js, callbacks for methods that accept them are optional but suggested
you omit the callback, errors will be thrown as exceptions). The first callback parameter is
reserved for passing errors. It will be `null` if no errors occurred and should always be checked.
#### Support for Promises
The Promise based API mimics the way Node [implements](https://nodejs.org/api/fs.html#fs_fs_promises_api) them. Both `Shell` and `FileSystem` now have a `promises` object attached alongside the regular callback style methods. Method names are identical to their callback counterparts with the difference that instead of receiving a final argument as a callback, they return a Promise that is resolved or rejected based on the success of method execution.
> Please note that `exists` method will always throw, since it was [deprecated](https://nodejs.org/api/fs.html#fs_fs_exists_path_callback) by Node.
See example below:
```javascript
const fs = new Filer.FileSystem().promises;
fs.open('/myfile', 'w+')
.then(fd => fs.close(fd))
.then(() => fs.stat('/myfile'))
.then(stats => { console.log(`stats: ${JSON.stringify(stats)}`); })
.catch(err => { console.error(err); })
```
> The callback style usage could be found [here](#overviewExample).
#### Filer.FileSystem(options, callback) constructor
File system constructor, invoked to open an existing file system or create a new one.

3517
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@
},
"dependencies": {
"base64-arraybuffer": "^0.1.5",
"es6-promisify": "^6.0.0",
"minimatch": "^3.0.4"
},
"devDependencies": {

View File

@ -1901,6 +1901,7 @@ function exists(fs, context, path, callback) {
function cb(err) {
callback(err ? false : true);
}
console.warn('This method is deprecated. For more details see https://nodejs.org/api/fs.html#fs_fs_exists_path_callback');// eslint-disable-line no-console
stat(fs, context, path, cb);
}

View File

@ -1,4 +1,5 @@
var _ = require('../../lib/nodash.js');
var { promisify } = require('es6-promisify');
var isNullPath = require('../path.js').isNull;
var nop = require('../shared.js').nop;
@ -262,94 +263,97 @@ function FileSystem(options, callback) {
impl.ensureRootDirectory(context, complete);
}
});
FileSystem.prototype.promises = {};
/**
* Public API for FileSystem
*/
[
'open',
'chmod',
'fchmod',
'chown',
'fchown',
'close',
'mknod',
'mkdir',
'rmdir',
'stat',
'fstat',
'link',
'unlink',
'read',
'readFile',
'write',
'writeFile',
'appendFile',
'exists',
'lseek',
'readdir',
'rename',
'readlink',
'symlink',
'lstat',
'truncate',
'ftruncate',
'utimes',
'futimes',
'setxattr',
'getxattr',
'fsetxattr',
'fgetxattr',
'removexattr',
'fremovexattr'
].forEach(function(methodName) {
FileSystem.prototype[methodName] = function() {
var fs = this;
var args = Array.prototype.slice.call(arguments, 0);
var lastArgIndex = args.length - 1;
// We may or may not get a callback, and since node.js supports
// fire-and-forget style fs operations, we have to dance a bit here.
var missingCallback = typeof args[lastArgIndex] !== 'function';
var callback = maybeCallback(args[lastArgIndex]);
var error = fs.queueOrRun(function() {
var context = fs.provider.openReadWriteContext();
// Fail early if the filesystem is in an error state (e.g.,
// provider failed to open.
if(FS_ERROR === fs.readyState) {
var err = new Errors.EFILESYSTEMERROR('filesystem unavailable, operation canceled');
return callback.call(fs, err);
}
// Wrap the callback so we can explicitly close the context
function complete() {
context.close();
callback.apply(fs, arguments);
}
// Either add or replace the callback with our wrapper complete()
if(missingCallback) {
args.push(complete);
} else {
args[lastArgIndex] = complete;
}
// Forward this call to the impl's version, using the following
// call signature, with complete() as the callback/last-arg now:
// fn(fs, context, arg0, arg1, ... , complete);
var fnArgs = [fs, context].concat(args);
impl[methodName].apply(null, fnArgs);
});
if(error) {
callback(error);
}
};
FileSystem.prototype.promises[methodName] = promisify(FileSystem.prototype[methodName].bind(fs));
});
}
// Expose storage providers on FileSystem constructor
FileSystem.providers = providers;
/**
* Public API for FileSystem
*/
[
'open',
'chmod',
'fchmod',
'chown',
'fchown',
'close',
'mknod',
'mkdir',
'rmdir',
'stat',
'fstat',
'link',
'unlink',
'read',
'readFile',
'write',
'writeFile',
'appendFile',
'exists',
'lseek',
'readdir',
'rename',
'readlink',
'symlink',
'lstat',
'truncate',
'ftruncate',
'utimes',
'futimes',
'setxattr',
'getxattr',
'fsetxattr',
'fgetxattr',
'removexattr',
'fremovexattr'
].forEach(function(methodName) {
FileSystem.prototype[methodName] = function() {
var fs = this;
var args = Array.prototype.slice.call(arguments, 0);
var lastArgIndex = args.length - 1;
// We may or may not get a callback, and since node.js supports
// fire-and-forget style fs operations, we have to dance a bit here.
var missingCallback = typeof args[lastArgIndex] !== 'function';
var callback = maybeCallback(args[lastArgIndex]);
var error = fs.queueOrRun(function() {
var context = fs.provider.openReadWriteContext();
// Fail early if the filesystem is in an error state (e.g.,
// provider failed to open.
if(FS_ERROR === fs.readyState) {
var err = new Errors.EFILESYSTEMERROR('filesystem unavailable, operation canceled');
return callback.call(fs, err);
}
// Wrap the callback so we can explicitly close the context
function complete() {
context.close();
callback.apply(fs, arguments);
}
// Either add or replace the callback with our wrapper complete()
if(missingCallback) {
args.push(complete);
} else {
args[lastArgIndex] = complete;
}
// Forward this call to the impl's version, using the following
// call signature, with complete() as the callback/last-arg now:
// fn(fs, context, arg0, arg1, ... , complete);
var fnArgs = [fs, context].concat(args);
impl[methodName].apply(null, fnArgs);
});
if(error) {
callback(error);
}
};
});
module.exports = FileSystem;

View File

@ -1,3 +1,4 @@
var {promisify} = require('es6-promisify');
var Path = require('../path.js');
var Errors = require('../errors.js');
var Environment = require('./environment.js');
@ -56,6 +57,24 @@ function Shell(fs, options) {
this.pwd = function() {
return cwd;
};
this.promises = {};
/**
* Public API for Shell converted to Promise based
*/
[
'cd',
'exec',
'touch',
'cat',
'ls',
'rm',
'tempDir',
'mkdirp',
'find'
].forEach((methodName)=>{
this.promises[methodName] = promisify(this[methodName].bind(this));
});
}
/**