[react-packager] onchange endpoint that informs of changes

This commit is contained in:
Amjad Masad 2015-03-06 14:26:01 -08:00
parent b9207a3095
commit c99284bfdf
2 changed files with 79 additions and 12 deletions

View File

@ -45,6 +45,7 @@ describe('processRequest', function() {
var invalidatorFunc = jest.genMockFunction();
var watcherFunc = jest.genMockFunction();
var requestHandler;
var triggerFileChange;
beforeEach(function() {
Packager = require('../../Packager');
@ -61,7 +62,15 @@ describe('processRequest', function() {
});
};
FileWatcher.prototype.on = watcherFunc;
FileWatcher.prototype.on = function(eventType, callback) {
if (eventType !== 'all') {
throw new Error('Can only handle "all" event in watcher.');
}
watcherFunc.apply(this, arguments);
triggerFileChange = callback;
return this;
};
Packager.prototype.invalidateFile = invalidatorFunc;
@ -109,17 +118,6 @@ describe('processRequest', function() {
describe('file changes', function() {
var triggerFileChange;
beforeEach(function() {
FileWatcher.prototype.on = function(eventType, callback) {
if (eventType !== 'all') {
throw new Error('Can only handle "all" event in watcher.');
}
triggerFileChange = callback;
return this;
};
});
pit('invalides files in package when file is updated', function() {
return makeRequest(
requestHandler,
@ -175,4 +173,36 @@ describe('processRequest', function() {
});
});
});
describe('/onchange endpoint', function() {
var EventEmitter;
var req;
var res;
beforeEach(function() {
EventEmitter = require.requireActual('events').EventEmitter;
req = new EventEmitter();
req.url = '/onchange';
res = {
writeHead: jest.genMockFn(),
end: jest.genMockFn()
};
});
it('should hold on to request and inform on change', function() {
server.processRequest(req, res);
triggerFileChange('all', 'path/file.js', options.projectRoots[0]);
jest.runAllTimers();
expect(res.end).toBeCalledWith(JSON.stringify({changed: true}));
});
it('should not inform changes on disconnected clients', function() {
server.processRequest(req, res);
req.emit('close');
jest.runAllTimers();
triggerFileChange('all', 'path/file.js', options.projectRoots[0]);
jest.runAllTimers();
expect(res.end).not.toBeCalled();
});
});
});

View File

@ -50,6 +50,7 @@ function Server(options) {
this._projectRoots = opts.projectRoots;
this._packages = Object.create(null);
this._packager = new Packager(opts);
this._changeWatchers = [];
this._fileWatcher = options.nonPersistent
? FileWatcher.createDummyWatcher()
@ -65,6 +66,7 @@ Server.prototype._onFileChange = function(type, filepath, root) {
// Make sure the file watcher event runs through the system before
// we rebuild the packages.
setImmediate(this._rebuildPackages.bind(this, absPath));
setImmediate(this._informChangeWatchers.bind(this));
};
Server.prototype._rebuildPackages = function() {
@ -83,6 +85,20 @@ Server.prototype._rebuildPackages = function() {
});
};
Server.prototype._informChangeWatchers = function() {
var watchers = this._changeWatchers;
var headers = {
'Content-Type': 'application/json; charset=UTF-8',
};
watchers.forEach(function(w) {
w.res.writeHead(205, headers);
w.res.end(JSON.stringify({ changed: true }));
});
this._changeWatchers = [];
};
Server.prototype.end = function() {
q.all([
this._fileWatcher.end(),
@ -142,6 +158,24 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
}
};
Server.prototype._processOnChangeRequest = function(req, res) {
var watchers = this._changeWatchers;
watchers.push({
req: req,
res: res,
});
req.on('close', function() {
for (var i = 0; i < watchers.length; i++) {
if (watchers[i] && watchers[i].req === req) {
watchers.splice(i, 1);
break;
}
}
});
};
Server.prototype.processRequest = function(req, res, next) {
var urlObj = url.parse(req.url, true);
var pathname = urlObj.pathname;
@ -154,6 +188,9 @@ Server.prototype.processRequest = function(req, res, next) {
} else if (pathname.match(/^\/debug/)) {
this._processDebugRequest(req.url, res);
return;
} else if (pathname.match(/^\/onchange\/?$/)) {
this._processOnChangeRequest(req, res);
return;
} else {
next();
return;