Repository: filerjs/filer
Pull request #651 (2019-01-03 02:13:30)

Add Filer.fs to support creating a filesystem like node.js
To be more in sync with how node.js works, this adds `Filer.fs`, which lazily creates a filesystem instance.  It also adds `Filer.path` to match what node devs would expect, and allows users to do the following:

```js
const { fs, Buffer, path } = require('filer');
```

I have kept `Filer.Path` to not break the API, but we could remove it later if we wanted.

I also spotted a stray WebSQL reference in the tests, which this also removes.

codecov-io: # [Codecov](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=h1) Report
> Merging [#651](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=desc) into [master](https://codecov.io/gh/filerjs/filer/commit/85a8c21dc1d31ad5c88d21be5320a4ba090032bf?src=pr&el=desc) will **decrease** coverage by `0.06%`.
> The diff coverage is `57.14%`.

[![Impacted file tree graph](https://codecov.io/gh/filerjs/filer/pull/651/graphs/tree.svg?width=650&token=JoQTgz1Un2&height=150&src=pr)](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #651      +/-   ##
==========================================
- Coverage    86.6%   86.54%   -0.07%     
==========================================
  Files          17       17              
  Lines        1740     1746       +6     
==========================================
+ Hits         1507     1511       +4     
- Misses        233      235       +2
```


| [Impacted Files](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/index.js](https://codecov.io/gh/filerjs/filer/pull/651/diff?src=pr&el=tree#diff-c3JjL2luZGV4Lmpz) | `57.14% <57.14%> (-42.86%)` | :arrow_down: |
| [src/filesystem/interface.js](https://codecov.io/gh/filerjs/filer/pull/651/diff?src=pr&el=tree#diff-c3JjL2ZpbGVzeXN0ZW0vaW50ZXJmYWNlLmpz) | `93.52% <0%> (+0.58%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=footer). Last update [85a8c21...7ada0d6](https://codecov.io/gh/filerjs/filer/pull/651?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

humphd: NOTE: the lower coverage on `src/index.js` is because we can't run this in our current coverage environment (node) because it depends on indexeddb--it's fine.

Patch:
diff --git a/README.md b/README.md
index 84e0f2e..f0dfb72 100644
--- a/README.md
+++ b/README.md
@@ -81,14 +81,27 @@ backend storage providers, for example `Memory`. See the section on [Storage Pro
 <a name="overviewExample"></a>
 
 ```js
-var fs = new Filer.FileSystem();
-fs.open('/myfile', 'w+', function(err, fd) {
-  if (err) throw err;
-  fs.close(fd, function(err) {
-    if (err) throw err;
-    fs.stat('/myfile', function(err, stats) {
-      if (err) throw err;
-      console.log('stats: ' + JSON.stringify(stats));
+const { fs, path } = require('filer');
+
+fs.mkdir('/docs', (err) => {
+  if (err) {
+    return console.error('Unable to create /docs dir', err);
+  }
+  
+  const filename = path.join('/docs', 'first.txt');
+  const data = 'Hello World!\n';
+
+  fs.writeFile(filename, data, (err) => {
+    if (err) {
+      return console.error('Unable to write /docs/first.txt', err);
+    }
+
+    fs.stat(filename, (err, stats) =>  {
+      if (err) {
+        return console.error('Unable to stat /docs/first.txt', err);
+      }
+
+      console.log('Stats for /docs/first.txt:', stats);
     });
   });
 });
@@ -125,9 +138,13 @@ fs.writeFile('/myfile', 'some data')
 
 #### Filer.FileSystem(options, callback) constructor
 
-File system constructor, invoked to open an existing file system or create a new one.
-Accepts two arguments: an `options` object, and an optional `callback`. The `options`
-object can specify a number of optional arguments, including:
+In most cases, using `Filer.fs` will be sufficient, and provide a working filesystem.
+However, if you need more control over the filesystem, you can also use the `FileSystem`
+constructor, invoked to open an existing file system or create a new one.
+
+`Filer.FileSystem()` It accepts two arguments: an `options` object, and an optional
+`callback` function. The `options` object can specify a number of optional arguments,
+including:
 
 * `name`: the name of the file system, defaults to `'"local'`
 * `flags`: an Array of one or more flags to use when creating/opening the file system:
@@ -228,15 +245,16 @@ Buffer.allocUnsafe(size)
 
 #### Filer.Path<a name="FilerPath"></a>
 
-The node.js [path module](http://nodejs.org/api/path.html) is available via the `Filer.Path` object. It is
-identical to the node.js (see [https://github.com/browserify/path-browserify](https://github.com/browserify/path-browserify)) version with the following differences:
+The node.js [path module](http://nodejs.org/api/path.html) is available via `Filer.path` or
+`Filer.Path` (both are supported for historical reasons, and to match node). The Filer `path`
+module is identical to the node.js version (see [https://github.com/browserify/path-browserify](https://github.com/browserify/path-browserify)), with the following differences:
 
 * The CWD always defaults to `/`
 * No support for Windows style paths (assume you are on a POSIX system)
 * Additional utility methods (see below)
 
 ```javascript
-var path = Filer.Path;
+var path = Filer.path;
 var dir = path.dirname('/foo/bar/baz/asdf/quux');
 // dir is now '/foo/bar/baz/asdf'
 
@@ -324,7 +342,11 @@ Once a `FileSystem` is created, it has the following methods. NOTE: code example
 a `FileSystem` instance named `fs` has been created like so:
 
 ```javascript
-var fs = new Filer.FileSystem();
+// 1. Using Filer.fs for a default filesystem
+const { fs } = require('filer');
+
+// 2. Or via the FileSystem constructor with specified options
+const fs = new Filer.FileSystem(options, callback);
 ```
 
 * [fs.rename(oldPath, newPath, callback)](#rename)
diff --git a/src/index.js b/src/index.js
index dc54a5e..c89fdf9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,24 @@
-module.exports = {
+let fs = null;
+let Filer = null;
+
+module.exports = Filer = {
   FileSystem: require('./filesystem/interface.js'),
   Buffer: Buffer,
+  // We previously called this Path, but node calls it path. Do both
   Path: require('./path.js'),
+  path: require('./path.js'),
   Errors: require('./errors.js'),
   Shell: require('./shell/shell.js')
 };
+
+// Add a getter for the `fs` instance, which returns
+// a Filer FileSystem instance, using the default provider/flags.
+Object.defineProperty(Filer, 'fs', {
+  enumerable: true,
+  get() {
+    if(!fs) {
+      fs = new Filer.FileSystem();
+    }
+    return fs;
+  }
+});


Test patch:
diff --git a/tests/index.js b/tests/index.js
index 2402fd3..db38549 100644
--- a/tests/index.js
+++ b/tests/index.js
@@ -85,3 +85,6 @@ require('./bugs/issue267.js');
 require('./bugs/issue270.js');
 require('./bugs/rename-dir-trailing-slash.js');
 require('./bugs/issue357.js');
+
+// Sample code from README
+require('./spec/readme.example.spec');
diff --git a/tests/spec/filer.spec.js b/tests/spec/filer.spec.js
index 2fec5ab..18cb8d8 100644
--- a/tests/spec/filer.spec.js
+++ b/tests/spec/filer.spec.js
@@ -10,6 +10,37 @@ describe('Filer', function() {
     expect(typeof Filer.FileSystem).to.equal('function');
   });
 
+  it('has Buffer constructor', function() {
+    expect(typeof Filer.Buffer).to.equal('function');
+  });
+
+  it('has Path and path objects', function() {
+    expect(typeof Filer.Path).to.equal('object');
+    expect(typeof Filer.path).to.equal('object');
+    expect(Filer.Path).to.equal(Filer.path);
+  });
+
+  it('has Errors object', function() {
+    expect(typeof Filer.Errors).to.equal('object');
+  });
+
+  it('has an fs object that returns a Filer.FileSystem', function() {
+    // Depends on IndexedDB being available, since we can't
+    // configure our own test provider.
+    if(!Filer.FileSystem.providers.IndexedDB.isSupported()) {
+      this.skip();
+    }
+
+    expect(typeof Filer.fs).to.equal('object');
+
+    const fs1 = Filer.fs;
+    const fs2 = Filer.fs;
+
+    expect(fs1).to.be.an.instanceof(Filer.FileSystem);
+    expect(fs2).to.be.an.instanceof(Filer.FileSystem);
+    expect(fs1).to.equal(fs2);
+  });
+
   it('has Shell constructor', function() {
     expect(typeof Filer.Shell).to.equal('function');
   });
@@ -24,8 +55,6 @@ describe('Filer', function() {
     var Provider;
     if(providers.IndexedDB.isSupported()) {
       Provider = providers.IndexedDB;
-    } else if(providers.WebSQL.isSupported()) {
-      Provider = providers.WebSQL;
     } else {
       Provider = providers.Memory;
     }
diff --git a/tests/spec/readme.example.spec.js b/tests/spec/readme.example.spec.js
new file mode 100644
index 0000000..7605b9a
--- /dev/null
+++ b/tests/spec/readme.example.spec.js
@@ -0,0 +1,43 @@
+const { path } = require('../../src');
+var util = require('../lib/test-utils.js');
+var expect = require('chai').expect;
+
+describe('README example code', function() {
+  beforeEach(util.setup);
+  afterEach(util.cleanup);
+
+  it('should run the code in the README overview example', function(done) {
+    // Slightly modified version of the first example code in the README
+    // See <a name="overviewExample"></a>
+    const fs = util.fs();
+
+    fs.mkdir('/docs', (err) => {
+      if (err) throw err;
+      
+      const filename = path.join('/docs', 'first.txt');
+      const data = 'Hello World!\n';
+    
+      fs.writeFile(filename, data, (err) => {
+        if (err) throw err;
+    
+        fs.stat(filename, (err, stats) => {
+          if (err) throw err;
+          expect(stats.size).to.equal(data.length);
+          done();
+        });
+      });
+    });
+  });
+
+  it('should run the fsPromises example code', function() {
+    const fs = util.fs().promises;
+    const filename = '/myfile';
+    const data = 'some data';
+
+    return fs.writeFile(filename, data)
+      .then(() => fs.stat(filename))
+      .then(stats => { 
+        expect(stats.size).to.equal(data.length);
+      });
+  });
+});