livereloadx
Version:
An implementation of the LiveReload 2 server in Node.js
120 lines (97 loc) • 3.27 kB
JavaScript
;
var EventEmitter = require('events').EventEmitter
, fs = require('fs')
, http = require('http')
, inject = require('./html').inject
, log = require('./log')('static')
, pause = require('pause')
, send = require('send')
, url = require('url')
, util = require('util');
// Static file handler
// onError: error callback (when an error occur, this callback is called and
// should call res.end())
function StaticHandler(config, onError) {
this.config = config;
this.onError = onError;
this.root = require('path').resolve(this.config.dir);
if (this.config.spa) {
this.is_included = require('./filter').getMatcher(this.config.filter);
log.info("Enabled SPA mode.");
} else if (this.config.static) {
log.info("Enabled static mode.");
}
log.debug('root dir: %s', this.root);
EventEmitter.call(this);
}
// inherits from EventEmitter
util.inherits(StaticHandler, EventEmitter);
// Handle request
StaticHandler.prototype.handle = function(req, res) {
if ('GET' !== req.method && 'HEAD' !== req.method) { return false; }
var path = url.parse(req.url).pathname;
var _pause = pause(req);
var self = this;
log.debug('path: %s', path);
var sendStream = send(req, path, {root: this.root});
if (path.match(/(\/|\.html?)$/)) {
// hook SendStream.send()
sendStream.send = function(path, stat) {
// HTML -> call sendWithInjection()
self.sendWithInjection(path, stat, this);
};
}
// Set error callback
sendStream.on('error', function(err) {
if (self.onError) {
self.onError(req, res, err);
// replay end & data events
// (ref) connect/lib/middlewares/static.js
// https://gist.github.com/mikedeboer/3047099
_pause.resume();
return;
}
// send index content in SPA mode
if (self.config.spa && !self.is_included(path)) {
var baseUrl = 'http://localhost:' + self.config.port + '/';
log.debug('spa: ' + path + ' to ' + baseUrl);
http.get(baseUrl, function(rootRes) {
res.setHeader('Content-Type', 'text/html');
res.writeHead(rootRes.statusCode);
rootRes
.on('data', function(data) { res.write(data); })
.on('end', function() { res.end(); });
});
return;
}
// fallback to normal 404
res.writeHead(404);
res.write('Not Found');
res.end();
});
sendStream
.on('end', function() { log.debug('end'); self.emit('end'); })
.pipe(res);
return true;
};
// Inserts LiveReload snippet and returns to client
StaticHandler.prototype.sendWithInjection = function(path, stat, stream) {
// read file to end
var self = this;
fs.readFile(path, function(err, data) {
if (err) {
stream.error(500, err);
return;
}
// set header
stream.setHeader(path, stat);
stream.res.setHeader('Content-Type', 'text/html');
// inject snippet
var buffer = inject(data, self.config.port);
// write to stream
stream.res.setHeader('Content-Length', buffer.length);
stream.res.write(buffer);
stream.res.end();
});
};
module.exports = StaticHandler;