diff --git a/README.md b/README.md
index 61e72bc1..34b3b6df 100644
--- a/README.md
+++ b/README.md
@@ -388,6 +388,7 @@ const fs = new Filer.FileSystem(options, callback);
* [fs.removexattr(path, name, callback)](#removexattr)
* [fs.fremovexattr(fd, name, callback)](#fremovexattr)
* [fs.watch(filename, [options], [listener])](#watch)
+* [fs.watchFile(filename, [options], [listener])](#watchFile)
#### fs.rename(oldPath, newPath, callback)
@@ -1323,6 +1324,24 @@ var watcher = fs.watch('/data', { recursive: true }, function(event, filename) {
fs.writeFile('/data/subdir/file', 'data');
```
+### fs.watchFile(filename, [options], [listener])
+
+Watch for changes on a file at `filename`. The callback `listener` will be called each time the file is accessed.
+
+The `options` argument only supports the change in interval between checks measured in milliseconds and does not support perstistence like node.
+
+The `listener` receives two arguments that are the current stat object and previous stat object that are instances of `fs.Stat`. Reference to `fs.Stat` can be found
+here: [`fs.Stat`](https://nodejs.org/api/fs.html#fs_class_fs_stats)
+
+Example:
+```javascript
+fs.watchFile('/myfile.txt', (curr, prev) => {
+ console.log(`the current mtime is: ${curr.mtime}`);
+ console.log(`the previous mtime was: ${prev.mtime}`);
+});
+```
+
+
### FileSystemShell
Many common file system shell operations are available by using a `FileSystemShell` object.
diff --git a/package-lock.json b/package-lock.json
index f699b11d..595abf43 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "filer",
- "version": "1.1.0",
+ "version": "1.1.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/src/filesystem/interface.js b/src/filesystem/interface.js
index 824128b3..859e9ad0 100644
--- a/src/filesystem/interface.js
+++ b/src/filesystem/interface.js
@@ -206,6 +206,60 @@ function FileSystem(options, callback) {
return watcher;
};
+ //Object that uses filenames as keys
+ const statWatchers = new Map();
+
+ this.watchFile = function (filename, options, listener) {
+ let prevStat, currStat;
+
+ if (Path.isNull(filename)) {
+ throw new Error('Path must be a string without null bytes.');
+ }
+ // Checks to see if there were options passed in and if not, the callback function will be set here
+ if (typeof options === 'function') {
+ listener = options;
+ options = {};
+ }
+ // default 5007ms interval, persistent is not used this project
+ const interval = options.interval || 5007;
+ listener = listener || nop;
+
+ let intervalValue = statWatchers.get(filename);
+
+ if(intervalValue) {
+ return;
+ }
+ else {
+ fs.stat(filename, function (err, stats) {
+ var value = setInterval(function () {
+ prevStat = currStat;
+
+ //Conditional check for first run to set initial state for prevStat
+ if(!prevStat) {
+ prevStat = stats;
+ }
+
+ currStat = stats;
+
+ if (err) {
+ clearInterval(value);
+ console.warn('[Filer Error] fs.watchFile encountered an error' + err.message);
+ }
+ if (JSON.stringify(prevStat) !== JSON.stringify(currStat)) {
+ listener(prevStat, currStat);
+ }
+ // Set a new prevStat based on previous
+ prevStat = currStat;
+ },
+ interval
+ );
+
+ // Stores interval return values
+ statWatchers.set(filename, value);
+ });
+ }
+ };
+
// Deal with various approaches to node ID creation
function wrappedGuidFn(context) {
return function (callback) {
diff --git a/tests/index.js b/tests/index.js
index beb517f2..62432455 100644
--- a/tests/index.js
+++ b/tests/index.js
@@ -41,6 +41,7 @@ require('./spec/trailing-slashes.spec');
require('./spec/times.spec');
require('./spec/time-flags.spec');
require('./spec/fs.watch.spec');
+require('./spec/fs.watchFile.spec');
require('./spec/fs.unwatchFile.spec');
require('./spec/errors.spec');
require('./spec/fs.shell.spec');
diff --git a/tests/spec/fs.watchFile.spec.js b/tests/spec/fs.watchFile.spec.js
new file mode 100644
index 00000000..2a59a2cd
--- /dev/null
+++ b/tests/spec/fs.watchFile.spec.js
@@ -0,0 +1,69 @@
+'use strict';
+
+var util = require('../lib/test-utils.js');
+var expect = require('chai').expect;
+
+describe('fs.watchFile', function() {
+ beforeEach(util.setup);
+ afterEach(util.cleanup);
+
+ it('should be a function', function() {
+ const fs = util.fs();
+ expect(typeof fs.watchFile).to.equal('function');
+ });
+
+ it('should throw an error if a file path is not defined', function() {
+ const fs = util.fs();
+
+ const fn = () => fs.watchFile(undefined);
+ expect(fn).to.throw();
+ });
+
+ it('should throw an error if a file is deleted', function() {
+ const fs = util.fs();
+
+ fs.writeFile('/myfile', 'data', function(error) {
+ if(error) throw error;
+ });
+
+ fs.watchFile('/myfile', function(prev, curr) {
+ expect(prev).to.exist;
+ expect(curr).to.exist;
+ });
+
+ fs.unlink('/myfile');
+
+ const fn = () => fs.watchFile('/myfile');
+ expect(fn).to.throw();
+ });
+
+ it('prev and curr should be equal if nothing has been changed in the file', function() {
+ const fs = util.fs();
+
+ fs.writeFile('/myfile', 'data', function(error) {
+ if(error) throw error;
+ });
+
+ fs.watchFile('/myfile', function(prev, curr) {
+ expect(prev).to.equal(curr);
+ });
+ });
+
+ it('prev and curr should not be equal if something has been changed in the file', function() {
+ const fs = util.fs();
+
+ fs.writeFile('/myfile', 'data', function(error) {
+ if(error) throw error;
+ });
+
+ fs.watchFile('/myfile', function(prev, curr) {
+ expect(prev).to.equal(curr);
+
+ fs.writeFile('/myfile', 'data2', function(error) {
+ if(error) throw error;
+ });
+
+ expect(prev).to.not.equal(curr);
+ });
+ });
+});
\ No newline at end of file