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'); } ); }); });