spoonjs
Version:
SpoonJS CLI tool
179 lines (154 loc) • 5.49 kB
JavaScript
/*jshint node:true, es3:false, latedef:false*/
;
var fs = require('fs');
var path = require('path');
var express = require('express');
var connect = require('connect');
module.exports = function (task) {
task
.id('server')
.name('Server')
.author('Indigo United')
.description('Run server')
.option('env', 'The environment that the server will run', 'dev')
.option('port', 'The port to listen for requests', 8080)
.option('host', 'The host to listen for requests', '127.0.0.1')
.setup(function (options, ctx, next) {
options.index = './index_' + options.env + '.html';
options.assetsDir = options.env;
if (options.env !== 'dev') {
options.rewrite = true;
options.gzip = true;
}
next();
})
.do(function (options, ctx, next) {
// Change cwd to the web folder
process.chdir('web');
var web = process.cwd(),
env = options.env,
link;
// Check if the env is valid
try {
fs.statSync(options.index);
} catch (e) {
if (e.code === 'ENOENT') {
return next(new Error('Invalid environment: ' + env));
}
}
options.web = web;
// Create dev symlink
if (options.env === 'dev') {
try {
link = fs.readlinkSync('dev');
} catch (e) {}
if (!link || link !== '..') {
try {
fs.unlinkSync('dev');
} catch (e) {}
// In windows, users can't create symlinks in the console
// without running the actual command with Administrator permissions
// see: http://ahtik.com/blog/2012/08/16/fixing-your-virtualbox-shared-folder-symlink-error
if (process.platform === 'win32') {
try {
fs.symlinkSync('..', 'dev', 'dir');
} catch (e) {
if (e.code === 'EPERM') {
return next(new Error('No permission to create symlink (try running as an Administrator).'));
}
}
} else {
fs.symlinkSync('..', 'dev', 'dir');
}
}
// Check assets dir
} else {
try {
fs.statSync(options.assetsDir);
} catch (e) {
if (e.code === 'ENOENT') {
return next(new Error('Directory "' + options.assetsDir + '" not found, did you forgot to build?'));
}
}
}
next();
}, {
description: 'Prepare server'
})
.do(function (opts, ctx) {
// Check if assets dir exists
var site = express();
// Enable compression?
if (opts.gzip) {
site.use(connect.compress());
}
// Serve utf-8 by default
site.use(function (req, res, next) {
res.charset = 'utf-8';
next();
});
// Serve index
site.get('/', function (req, res) {
return res.sendfile(opts.index);
});
// Serve favicon.ico
site.use(express.favicon('./favicon.ico'));
// Serve files & folders
site.get('/*', function (req, res) {
// Get the requested file
var file = path.join(opts.web, req.url.substr(1));
// If there are query parameters, remove them
// Also decode the string
file = decodeURI(file.split('?')[0]);
fs.stat(file, function (err, stat) {
// If file does not exists
if (err) {
if (err.code === 'ENOENT') {
inexistentFile(file, opts, res, ctx);
} else {
res.send(500);
}
// If it exists and is a file, serve it
} else if (stat.isFile()) {
res.sendfile(file);
// Otherwise is a folder, so we deny the access
} else {
res.send(403);
}
});
});
// Effectively listen
site.listen(opts.port, opts.host);
ctx.log.writeln('Listening on http://' + (opts.host === '127.0.0.1' ? 'localhost' : opts.host) + ':' + opts.port + ' (' + opts.env + ' environment)');
}, {
description: 'Serve files'
});
};
/**
* Handles a request to an inexistent file.
*
* @param {Object} file The request file
* @param {Object} options The task options
* @param {Object} res The express response object
* @param {Object} ctx The automaton context
*/
function inexistentFile(file, options, res, ctx) {
// If the rewrite is disabled, we attempt to serve the 404.html page
if (!options.rewrite) {
var file404 = path.join(options.web, '404.html');
fs.stat(file404, function (err) {
if (!err) {
res.status(404);
res.sendfile('404.html');
} else {
res.send(404);
}
});
// Otherwise we rewrite to the front controller (index)
} else {
if (/\.[a-z0-9]{1,4}$/i.test(file)) {
ctx.log.warnln('Could not locate file', file);
}
res.sendfile(options.index);
}
}