Skip to content

Commit

Permalink
Add JS wrapper around C++ binding (#14)
Browse files Browse the repository at this point in the history
* Add JS wrapper around C++ binding

* Resolve ignore paths relative to watched directory
  • Loading branch information
devongovett authored Apr 5, 2019
1 parent cb1bf1a commit c016a4e
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 36 deletions.
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ const watcher = require('@parcel/watcher');
const path = require('path');

// Subscribe to events
watcher.subscribe(process.cwd(), (events) => {
let subscription = await watcher.subscribe(process.cwd(), (events) => {
console.log(events);
});

// later on...
await subscription.unsubscribe();

// Get events since some saved snapshot in the past
let snapshotPath = path.join(process.cwd(), 'snapshot.txt');
let events = watcher.getEventsSince(process.cwd(), snapshotPath);
let events = await watcher.getEventsSince(process.cwd(), snapshotPath);

// Save a snapshot for later
watcher.writeSnapshot(process.cwd(), snapshotPath);
await watcher.writeSnapshot(process.cwd(), snapshotPath);
```

## Watching
Expand All @@ -38,11 +41,23 @@ Events are throttled and coalesced for performance during large changes like `gi

Only one notification will be emitted per file. For example, if a file was both created and updated since the last event, you'll get only a `create` event. If a file is both created and deleted, you will not be notifed of that file. Renames cause two events: a `delete` for the old name, and a `create` for the new name.

```javascript
let subscription = await watcher.subscribe(process.cwd(), (events) => {
console.log(events);
});
```

Events have two properties:

* `type` - the event type: `create`, `update`, or `delete`.
* `path` - the absolute realpath to the file.

To unsubscribe from change notifications, call the `unsubscribe` method on the returned subscription object.

```javascript
await subscription.unsubscribe();
```

`@parcel/watcher` has the following watcher backends, listed in priority order:

* [FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS
Expand All @@ -59,13 +74,13 @@ You can specify the exact backend you wish to use by passing the `backend` optio
In order to query for historical changes, you first need a previous snapshot to compare to. This can be saved to a file with the `writeSnapshot` function, e.g. just before your program exits.

```javascript
watcher.writeSnapshot(dirPath, snapshotPath);
await watcher.writeSnapshot(dirPath, snapshotPath);
```

When your program starts up, you can query for changes that have occurred since that snapshot using the `getEventsSince` function.

```javascript
let events = watcher.getEventsSince(dirPath, snapshotPath);
let events = await watcher.getEventsSince(dirPath, snapshotPath);
```

The events returned are exactly the same as the events that would be passed to the `subscribe` callback (see above).
Expand All @@ -87,7 +102,7 @@ You can specify the exact backend you wish to use by passing the `backend` optio

All of the APIs in `@parcel/watcher` support the following options, which are passed as an object as the last function argument.

* `ignore` - an array of paths to ignore. They can be either files or directories. No events will be emitted about these files or directories or their children. They must be absolute paths.
* `ignore` - an array of paths to ignore. They can be either files or directories. No events will be emitted about these files or directories or their children.
* `backend` - the name of an explicitly chosen backend to use. Allowed options are `"fs-events"`, `"watchman"`, `"inotify"`, `"windows"`, or `"brute-force"` (only for querying). If the specified backend is not available on the current platform, the default backend will be used instead.

## License
Expand Down
37 changes: 36 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1 +1,36 @@
module.exports = require('bindings')('fschanges.node');
const binding = require('bindings')('fschanges.node');
const path = require('path');

function normalizeOptions(dir, opts = {}) {
if (Array.isArray(opts.ignore)) {
opts = Object.assign({}, opts, {
ignore: opts.ignore.map(ignore => path.resolve(dir, ignore))
});
}

return opts;
}

exports.writeSnapshot = (dir, snapshot, opts) => {
return binding.writeSnapshot(path.resolve(dir), path.resolve(snapshot), normalizeOptions(dir, opts));
};

exports.getEventsSince = (dir, snapshot, opts) => {
return binding.getEventsSince(path.resolve(dir), path.resolve(snapshot), normalizeOptions(dir, opts));
};

exports.subscribe = async (dir, fn, opts) => {
dir = path.resolve(dir);
opts = normalizeOptions(dir, opts);
await binding.subscribe(dir, fn, opts);

return {
unsubscribe() {
return binding.unsubscribe(dir, fn, opts);
}
};
};

exports.unsubscribe = (dir, fn, opts) => {
return binding.unsubscribe(path.resolve(dir), fn, normalizeOptions(dir, opts));
};
50 changes: 21 additions & 29 deletions test/watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ describe('watcher', () => {

let c = 0;
const getFilename = (...dir) => path.join(tmpDir, ...dir, `test${c++}${Math.random().toString(31).slice(2)}`);
let ignoreDir, ignoreFile;
let ignoreDir, ignoreFile, sub;

before(async () => {
tmpDir = path.join(fs.realpathSync(require('os').tmpdir()), Math.random().toString(31).slice(2));
fs.mkdirpSync(tmpDir);
ignoreDir = getFilename();
ignoreFile = getFilename();
await new Promise(resolve => setTimeout(resolve, 100));
await fschanges.subscribe(tmpDir, fn, {backend, ignore: [ignoreDir, ignoreFile]});
sub = await fschanges.subscribe(tmpDir, fn, {backend, ignore: [ignoreDir, ignoreFile]});
});

after(async () => {
await fschanges.unsubscribe(tmpDir, fn, {backend, ignore: [ignoreDir, ignoreFile]});
await sub.unsubscribe();
});

describe('files', () => {
Expand Down Expand Up @@ -442,13 +442,11 @@ describe('watcher', () => {
await new Promise(resolve => setTimeout(resolve, 100));

function listen() {
return new Promise(resolve => {
let fn = events => {
return new Promise(async resolve => {
let sub = await fschanges.subscribe(dir, async events => {
setImmediate(() => resolve(events));
fschanges.unsubscribe(dir, fn, {backend});
};

fschanges.subscribe(dir, fn, {backend});
await sub.unsubscribe();
}, {backend});
});
}

Expand All @@ -471,13 +469,11 @@ describe('watcher', () => {
await new Promise(resolve => setTimeout(resolve, 100));

function listen(ignore) {
return new Promise(resolve => {
let fn = events => {
return new Promise(async resolve => {
let sub = await fschanges.subscribe(dir, async events => {
setImmediate(() => resolve(events));
fschanges.unsubscribe(dir, fn, {backend, ignore});
};

fschanges.subscribe(dir, fn, {backend, ignore});
await sub.unsubscribe();
}, {backend, ignore});
});
}

Expand All @@ -503,13 +499,11 @@ describe('watcher', () => {
await new Promise(resolve => setTimeout(resolve, 100));

function listen(dir) {
return new Promise(resolve => {
let fn = events => {
return new Promise(async resolve => {
let sub = await fschanges.subscribe(dir, async events => {
setImmediate(() => resolve(events));
fschanges.unsubscribe(dir, fn, {backend});
};

fschanges.subscribe(dir, fn, {backend});
await sub.unsubscribe();
}, {backend});
});
}

Expand All @@ -534,12 +528,10 @@ describe('watcher', () => {
await new Promise(resolve => setTimeout(resolve, 100));

function listen(dir) {
return new Promise(resolve => {
let fn = events => {
setImmediate(() => resolve([events, fn]));
};

fschanges.subscribe(dir, fn, {backend});
return new Promise(async resolve => {
let sub = await fschanges.subscribe(dir, events => {
setImmediate(() => resolve([events, sub]));
});
});
}

Expand All @@ -555,7 +547,7 @@ describe('watcher', () => {
await fs.writeFile(path.join(dir, 'test2.txt'), 'hello2');
await new Promise(resolve => setTimeout(resolve, 100));

let [watched, fn] = await l;
let [watched, sub] = await l;
assert.deepEqual(watched, [
{type: 'create', path: path.join(dir, 'test1.txt')}
]);
Expand All @@ -565,7 +557,7 @@ describe('watcher', () => {
{type: 'create', path: path.join(dir, 'test2.txt')}
]);

fschanges.unsubscribe(dir, fn, {backend});
await sub.unsubscribe();
});
});
});
Expand Down

0 comments on commit c016a4e

Please sign in to comment.