moved devServer into a module so that it can be used programmatically

This commit is contained in:
Spencer Alger 2014-06-12 15:51:06 -07:00
parent cac3fd6dbb
commit 50f0368a72
7 changed files with 120 additions and 82 deletions

View file

@ -6,22 +6,22 @@
"main": "Gulpfile.js",
"dependencies": {},
"devDependencies": {
"bluebird": "~2.0.7",
"connect": "~2.19.5",
"event-stream": "~3.1.5",
"expect.js": "~0.2.0",
"grunt": "~0.4.2",
"grunt-cli": "~0.1.13",
"grunt-contrib-connect": "~0.6.0",
"grunt-contrib-jade": "~0.10.0",
"grunt-contrib-jshint": "~0.8.0",
"grunt-contrib-less": "~0.10.0",
"grunt-contrib-requirejs": "~0.4.4",
"grunt-contrib-watch": "~0.5.3",
"grunt-mocha": "~0.4.10",
"http-proxy": "~1.1.4",
"istanbul": "~0.2.4",
"load-grunt-config": "~0.7.0",
"lodash": "~2.4.1",
"bluebird": "~1.2.4",
"mocha": "~1.17.1",
"event-stream": "~3.1.5"
"mocha": "~1.17.1"
},
"scripts": {
"test": "grunt test",

View file

@ -1,75 +0,0 @@
module.exports = function (grunt) {
var instrumentationMiddleware = require('../utils/instrumentation');
var amdRapperMiddleware = require('../utils/amd_rapper');
return {
dev: {
options: {
hostname: '0.0.0.0',
middleware: function (connect, options, stack) {
stack = stack || [];
var root = grunt.config.get('root');
// when a request for an intrumented file comes in (?instrument=true)
// and it is included in `pattern`, it will be handled
// by this middleware
stack.push(instrumentationMiddleware({
// root that files should be served from
root: root,
// make file names easier to read
displayRoot: grunt.config.get('src'),
// filter the filenames that will be served
filter: function (filename) {
// return true if the filename should be
// included in the coverage report (results are cached)
return grunt.file.isMatch([
'**/src/**/*.js',
'!**/src/bower_components/**/*',
'!**/src/kibana/utils/{event_emitter,next_tick}.js'
], filename);
}
}));
// minimize code duplication (especially in the istanbul reporter)
// by allowing node_modules to be requested in an AMD rapper
stack.push(amdRapperMiddleware({
root: root
}));
// standard static middleware reading from the root
stack.push(connect.static(root));
stack.push(function (req, res, next) {
if (req.method !== 'HEAD' || req.url !== '/') return next();
res.statusCode === 200;
res.setHeader('Pong', 'Kibana 4 Dev Server');
res.end();
});
// redirect requests for '/' to '/src/'
stack.push(function (req, res, next) {
if (req.url !== '/') return next();
res.statusCode = 303;
res.setHeader('Location', '/src/');
res.end();
});
// allow browsing directories
stack.push(
function (req, res, next) {
// prevent chrome's stupid "this page is in spanish"
res.setHeader('Content-Language', 'en');
next();
},
connect.directory(root)
);
return stack;
}
}
}
};
};

View file

@ -19,7 +19,7 @@ module.exports = function (grunt) {
}
function onError() {
grunt.task.run(['connect:dev']);
grunt.task.run(['server']);
done();
}

View file

@ -1,3 +1,10 @@
module.exports = function (grunt) {
grunt.registerTask('server', ['connect:dev:keepalive']);
grunt.registerTask('server', function () {
var done = this.async();
var DevServer = require('../test/utils/dev_server');
var server = new DevServer();
server.listen(8000, function () {
console.log('visit http://localhost:8000');
});
});
};

View file

@ -0,0 +1,106 @@
/* jshint node:true */
var connect = require('connect');
var http = require('http');
var Promise = require('bluebird');
var instrumentationMiddleware = require('./_instrumentation');
var amdRapperMiddleware = require('./_amd_rapper');
var rel = require('path').join.bind(null, __dirname);
var proxy = require('http-proxy').createProxyServer({});
var ROOT = rel('../../../');
var SRC = rel('../../../src');
module.exports = function DevServer(opts) {
opts = opts || {};
var server = this;
var app = connect();
var httpServer = http.createServer(app);
app.use(instrumentationMiddleware({
root: ROOT,
displayRoot: SRC,
filter: function (filename) {
return filename.match(/.*\/src\/.*\.js$/)
&& !filename.match(/.*\/src\/bower_components\/.*\.js$/)
&& !filename.match(/.*\/src\/kibana\/utils\/(event_emitter|next_tick)\.js$/);
}
}));
app.use(amdRapperMiddleware({
root: ROOT
}));
app.use('/es-proxy', function (req, res) {
req.url = req.url.replace(/^\/es-proxy/, '');
proxy.web(req, res, { target: 'http://localhost:' + (process.env.ES_PORT || 9200) });
});
app.use(connect.static(ROOT));
// respond to the "maybe_start_server" pings
app.use(function (req, res, next) {
if (req.method !== 'HEAD' || req.url !== '/') return next();
res.statusCode === 200;
res.setHeader('Pong', 'Kibana 4 Dev Server');
res.end();
});
app.use(function (req, res, next) {
if (req.url !== '/') return next();
res.statusCode = 303;
res.setHeader('Location', '/src/');
res.end();
});
// prevent chrome's stupid "this page is in spanish" on the directories page
app.use(function (req, res, next) {
res.setHeader('Content-Language', 'en');
next();
});
// allow browsing directories
app.use(connect.directory(ROOT));
server.listenOnFirstOpenPort = function (ports) {
var options = ports.slice(0);
// wrap this logic in an IIFE so that we can call it again later
return (function attempt() {
var port = options.shift();
if (!port) return Promise.reject(new Error('None of the supplied options succeeded'));
return server.listen(port)
// filter out EADDRINUSE errors and call attempt again
.catch(function (err) {
if (err.code === 'EADDRINUSE') return attempt();
throw err;
});
})();
};
server.listen = function (port) {
return new Promise(function (resolve, reject) {
var done = function (err) {
httpServer.removeListener('error', done);
httpServer.removeListener('listening', done);
// pass the error along
if (err) return reject(err);
resolve(server.port = httpServer.address().port);
};
// call done with an error
httpServer.on('error', done, true);
// call done without any args
httpServer.on('listening', done, true);
httpServer.listen(port);
});
};
server.close = httpServer.close.bind(httpServer);
};