Socket
Socket
Sign inDemoInstall

fb-watchman

Package Overview
Dependencies
1
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.0 to 1.3.0

112

index.js

@@ -141,11 +141,36 @@ /* Copyright 2014-present Facebook, Inc.

// already running.
var watchmanCommand = this.watchmanBinaryPath + ' get-sockname';
childProcess.exec(watchmanCommand,
function(error, stdout, stderr) {
if (error) {
self.emit('error', error);
var args = ['--no-pretty', 'get-sockname'];
// We use the more elaborate spawn rather than exec because there
// are some error cases on Windows where process spawning can hang.
// It is desirable to pipe stderr directly to stderr live so that
// we can discover the problem.
proc = childProcess.spawn(this.watchmanBinaryPath, args, {
stdio: ['ignore', 'pipe', 'pipe']
});
var stdout = [];
var stderr = [];
proc.stdout.on('data', function(data) {
stdout.push(data);
});
proc.stderr.on('data', function(data) {
data = data.toString('utf8');
stderr.push(data);
console.log(data);
});
proc.on('close', function (code) {
proc.stderr.end();
proc.stdout.end();
if (code !== 0) {
var why = this.watchmanBinaryPath + args.join(' ') +
' returned with exit code ' + code + ' ' +
stderr.join('');
console.log(why);
self.emit('error', why);
return;
}
try {
var obj = JSON.parse(stdout);
var obj = JSON.parse(stdout.join(''));
if ('error' in obj) {

@@ -184,2 +209,77 @@ error = new Error(obj.error);

cap_versions = {
"cmd-watch-del-all": "3.1.1",
"cmd-watch-project": "3.1",
"relative_root": "3.3",
"term-dirname": "3.1",
"term-idirname": "3.1",
"wildmatch": "3.7",
}
// Compares a vs b, returns < 0 if a < b, > 0 if b > b, 0 if a == b
function vers_compare(a, b) {
a = a.split('.');
b = b.split('.');
for (i = 0; i < 3; i++) {
d = parseInt(a[i] || '0') - parseInt(b[i] || '0');
if (d != 0) {
return d;
}
}
return 0; // Equal
}
function have_cap(vers, name) {
if (name in cap_versions) {
return vers_compare(vers, cap_versions[name]) >= 0;
}
return false;
}
// This is a helper that we expose for testing purposes
Client.prototype._synthesizeCapabilityCheck = function(
resp, optional, required) {
resp.capabilities = {}
version = resp.version;
optional.forEach(function (name) {
resp.capabilities[name] = have_cap(version, name);
});
required.forEach(function (name) {
var have = have_cap(version, name);
resp.capabilities[name] = have;
if (!have) {
resp.error = 'client required capability `' + name +
'` is not supported by this server';
}
});
return resp;
}
Client.prototype.capabilityCheck = function(caps, done) {
var optional = caps.optional || [];
var required = caps.required || [];
var self = this;
this.command(['version', {
optional: optional,
required: required
}], function (error, resp) {
if (error) {
done(error);
return;
}
if (!('capabilities' in resp)) {
// Server doesn't support capabilities, so we need to
// synthesize the results based on the version
resp = self._synthesizeCapabilityCheck(resp, optional, required);
if (resp.error) {
error = new Error(resp.error);
error.watchmanResponse = resp;
done(error);
return;
}
}
done(null, resp);
});
}
// Close the connection to the service

@@ -186,0 +286,0 @@ Client.prototype.end = function() {

2

package.json
{
"name": "fb-watchman",
"version": "1.2.0",
"version": "1.3.0",
"description": "Bindings for the Watchman file watching service",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -14,3 +14,5 @@ # fb-watchman

You should [install Watchman](https://facebook.github.io/watchman/docs/install.html) to make the most of this module.
You should [install Watchman](
https://facebook.github.io/watchman/docs/install.html) to make the most of this
module.

@@ -31,292 +33,4 @@ Then simply:

## Usage
## How do I use it?
```
var watchman = require('fb-watchman');
var client = new watchman.Client();
client.on('end', function() {
// Called when the connection to watchman is terminated
console.log('client ended');
});
client.on('error', function(error) {
console.error('Error while talking to watchman: ', error);
});
client.command(['version'], function(error, resp) {
if (error) {
console.error('Error getting version:', error);
return;
}
console.log('Talking to watchman version', resp.version);
});
// Example of the error case
client.command(['invalid-command-never-will-work'], function(error, resp) {
if (error) {
console.error('failed to subscribe: ', error);
return;
}
});
// Initiate a watch. You can repeatedly ask to watch the same dir without
// error; Watchman will re-use an existing watch.
client.command(['watch-project', process.cwd()], function(error, resp) {
if (error) {
console.error('Error initiating watch:', error);
return;
}
// It is considered to be best practice to show any 'warning' or 'error'
// information to the user, as it may suggest steps for remediation
if ('warning' in resp) {
console.log('warning: ', resp.warning);
}
// The default subscribe behavior is to deliver a list of all current files
// when you first subscribe, so you don't need to walk the tree for yourself
// on startup. If you don't want this behavior, you should issue a `clock`
// command and use it to give a logical time constraint on the subscription.
// See further below for an example of this.
// watch-project may re-use an existing watch at a higher level in the
// filesystem. It will tell us the relative path to the directory that
// we expressed interest in, so we need to adjust for it in our results
var path_prefix = '';
var root = resp['watch'];
if ('relative_path' in resp) {
path_prefix = resp['relative_path'];
console.log('(re)using project watch at ', root, ', our dir is relative: ',
path_prefix);
}
function get_relative_name(proj_rel) {
if (path_prefix.length == 0) {
return proj_rel;
}
if (proj_rel.substr(0, path_prefix.length) == path_prefix) {
return proj_rel.substr(path_prefix.length + 1);
}
return null;
}
// Subscribe to notifications about .js files
// https://facebook.github.io/watchman/docs/cmd/subscribe.html
client.command(['subscribe', root, 'mysubscription', {
// Match any .js file under process.cwd()
// https://facebook.github.io/watchman/docs/file-query.html#expressions
// Has more on the supported expression syntax
expression: ["allof",
["match", "*.js"],
// focus on the relative path from the project to the path
// of interest
['dirname', path_prefix]
],
// Which fields we're interested in
fields: ["name", "size", "exists", "type"]
}],
function(error, resp) {
if (error) {
// Probably an error in the subscription criteria
console.error('failed to subscribe: ', error);
return;
}
console.log('subscription ' + resp.subscribe + ' established');
}
);
// Subscription results are emitted via the subscription event.
// Note that this emits for all subscriptions. If you have
// subscriptions with different `fields` you will need to check
// the subscription name and handle the differing data accordingly
client.on('subscription', function(resp) {
// Each entry in `resp.files` will have the fields you requested
// in your subscription. The default is:
// { name: 'example.js',
// size: 1680,
// new: true,
// exists: true,
// mode: 33188 }
//
// Names are relative to resp.root; join them together to
// obtain a fully qualified path.
//
// `resp` looks like this in practice:
//
// { root: '/private/tmp/foo',
// subscription: 'mysubscription',
// files: [ { name: 'node_modules/fb-watchman/index.js',
// size: 4768,
// exists: true,
// mode: 33188 } ] }
console.log(resp.root, resp.subscription);
for (var i in resp.files) {
var f = resp.files[i];
// Fixup name for watch-project offset
if (resp.subscription == 'mysubscription') {
// we requested a set of fields in this subscription
f.name = get_relative_name(f.name);
} else {
// the other subscription we set up returns only the name
f = get_relative_name(f);
}
console.log(f);
}
});
// Here's an example of just subscribing for notifications after the
// current point in time
client.command(['clock', root], function(error, resp) {
if (error) {
console.error('Failed to query clock:', error);
return;
}
client.command(['subscribe', root, 'sincesub', {
expression: ['allof',
["match", "*.js"],
// focus on the relative path from the project to the path
// of interest
['dirname', path_prefix]
],
// Note: since we only request a single field, the `sincesub` subscription
// response will just set files to an array of filenames, not an array of
// objects with name properties
// { root: '/private/tmp/foo',
// subscription: 'sincesub',
// files: [ 'node_modules/fb-watchman/index.js' ] }
fields: ["name"],
since: resp.clock // time constraint
}],
function(error, resp) {
if (error) {
console.error('failed to subscribe: ', error);
return;
}
console.log('subscription ' + resp.subscribe + ' established');
}
);
});
});
```
## Methods
### client.command(args [, done])
Sends a command to the watchman service. `args` is an array that specifies
the command name and any optional arguments. The command is queued and
dispatched asynchronously. You may queue multiple commands to the service;
they will be dispatched in FIFO order once the client connection is established.
The `done` parameter is a callback that will be passed (error, result) when the
command completes. You may omit it if you are not interested in the result of
the command.
```
client.command(['watch-project', process.cwd()], function(error, resp) {
if (error) {
console.log('watch failed: ', error);
return;
}
if ('warning' in resp) {
console.log('warning: ', resp.warning);
}
if ('relative_path' in resp) {
// We will need to remember and adjust for relative_path
console.log('watching project ', resp.watch, ' relative path to cwd is ',
resp.relative_path);
} else {
console.log('watching ', resp.watch);
}
});
```
If a field named `warning` is present in `resp`, the watchman service is trying
to communicate an issue that the user should see and address. For example, if
the system watch resources need adjustment, watchman will provide information
about this and how to remediate the issue. It is suggested that tools that
build on top of this library bubble the warning message up to the user.
### client.end()
Terminates the connection to the watchman service. Does not wait
for any queued commands to send.
## Events
The following events are emitted by the watchman client object:
### Event: 'connect'
Emitted when the client successfully connects to the watchman service
### Event: 'error'
Emitted when the socket to the watchman service encounters an error.
It may also be emitted prior to establishing a connection if we are unable
to successfully execute the watchman CLI binary to determine how to talk
to the server process.
It is passed a variable that encapsulates the error.
### Event: 'end'
Emitted when the socket to the watchman service is closed
### Event: 'log'
Emitted in response to a unilateral `log` PDU from the watchman service.
To enable these, you need to send a `log-level` command to the service:
```
// This is very verbose, you probably don't want to do this
client.command(['log-level', 'debug']);
client.on('log', function(info) {
console.log(info);
});
```
### Event: 'subscription'
Emitted in response to a unilateral `subscription` PDU from the watchman
service. To enable these, you need to send a `subscribe` command to the service:
```
// Subscribe to notifications about .js files
client.command(['subscribe', process.cwd(), 'mysubscription', {
expression: ["match", "*.js"]
}],
function(error, resp) {
if (error) {
// Probably an error in the subscription criteria
console.log('failed to subscribe: ', error);
return;
}
console.log('subscription ' + resp.subscribe + ' established');
}
);
// Subscription results are emitted via the subscription event.
// Note that watchman will deliver a list of all current files
// when you first subscribe, so you don't need to walk the tree
// for yourself on startup
client.on('subscription', function(resp) {
console.log(resp.root, resp.subscription, resp.files);
});
```
To cancel a subscription, use the `unsubscribe` command and pass in the name of
the subscription you want to cancel:
```
client.command(['unsubscribe', process.cwd(), 'mysubscription']);
```
Note that subscriptions names are scoped to your connection to the watchman
service; multiple different clients can use the same subscription name without
fear of colliding.
[Read the NodeJS watchman documentation](https://facebook.github.io/watchman/docs/nodejs.html)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc