Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 427 lines (370 sloc) 8.682 kB
#!/usr/bin/env node
/**
* Module dependencies.
*/
var fs = require('fs')
, sys = require('sys')
, exec = require('child_process').exec;
/**
* Framework version.
*/
var version = '1.0.4';
/**
* stdin stream.
*/
var stdin;
/**
* Add session support.
*/
var sessions = false;
/**
* CSS engine to utilize.
*/
var cssEngine;
/**
* Template engine to utilize.
*/
var templateEngine = 'jade';
/**
* Usage documentation.
*/
var usage = ''
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
+ '\n'
+ '\x1b[1mOptions\x1b[0m:\n'
+ ' -s, --sessions Add session support\n'
+ ' -t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade\n'
+ ' -c, --css ENGINE Add stylesheet ENGINE support (less|sass). Defaults to plain css\n'
+ ' -v, --version Output framework version\n'
+ ' -h, --help Output help information\n'
;
/**
* Jade layout template.
*/
var jadeLayout = [
'!!!'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body!= body'
].join('\n');
/**
* Jade index template.
*/
var jadeIndex = [
'h1= title'
, 'p Welcome to #{title}'
].join('\n');
/**
* EJS layout template.
*/
var ejsLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title><%= title %></title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <%- body %>'
, ' </body>'
, '</html>'
].join('\n');
/**
* EJS index template.
*/
var ejsIndex = [
'<h1><%= title %></h1>'
, '<p>Welcome to <%= title %></p>'
].join('\n');
/**
* Default css template.
*/
var css = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
].join('\n');
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
].join('\n');
/**
* Default sass template.
*/
var sass = [
'body'
, ' :padding 50px'
, ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
].join('\n');
/**
* App test template.
*/
var appTest = [
""
, "// Run $ expresso"
, ""
, "/**"
, " * Module dependencies."
, " */"
, ""
, "var app = require('../app')"
, " , assert = require('assert');"
, "",
, "module.exports = {"
, " 'GET /': function(){"
, " assert.response(app,"
, " { url: '/' },"
, " { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }},"
, " function(res){"
, " assert.includes(res.body, '<title>Express</title>');"
, " });"
, " }"
, "};"
].join('\n');
/**
* App template.
*/
var app = [
''
, '/**'
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, ''
, 'var app = module.exports = express.createServer();'
, ''
, '// Configuration'
, ''
, 'app.configure(function(){'
, ' app.set(\'views\', __dirname + \'/views\');'
, ' app.set(\'view engine\', \':TEMPLATE\');'
, ' app.use(express.bodyDecoder());'
, ' app.use(express.methodOverride());:SESS:CSS'
, ' app.use(app.router);'
, ' app.use(express.staticProvider(__dirname + \'/public\'));'
, '});'
, ''
, 'app.configure(\'development\', function(){'
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
, '});'
, ''
, 'app.configure(\'production\', function(){'
, ' app.use(express.errorHandler()); '
, '});'
, ''
, '// Routes'
, ''
, 'app.get(\'/\', function(req, res){'
, ' res.render(\'index\', {'
, ' locals: {'
, ' title: \'Express\''
, ' }'
, ' });'
, '});'
, ''
, '// Only listen on $ node app.js'
, ''
, 'if (!module.parent) {'
, ' app.listen(3000);'
, ' console.log("Express server listening on port %d", app.address().port)'
, '}'
, ''
].join('\n');
// Parse arguments
var args = process.argv.slice(2)
, path = '.';
while (args.length) {
var arg = args.shift();
switch (arg) {
case '-h':
case '--help':
abort(usage);
break;
case '-v':
case '--version':
abort(version);
break;
case '-s':
case '--session':
case '--sessions':
sessions = true;
break;
case '-c':
case '--css':
args.length
? (cssEngine = args.shift())
: abort('--css requires an argument');
break;
case '-t':
case '--template':
args.length
? (templateEngine = args.shift())
: abort('--template requires an argument');
break;
default:
path = arg;
}
}
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty) {
createApplicationAt(path);
} else {
confirm('destination is not empty, continue? ', function(ok){
if (ok) {
stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
mkdir(path, function(){
mkdir(path + '/pids');
mkdir(path + '/logs');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (cssEngine) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'sass':
write(path + '/public/stylesheets/style.sass', sass);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/views/partials', function(){
switch (templateEngine) {
case 'ejs':
write(path + '/views/layout.ejs', ejsLayout);
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
}
});
mkdir(path + '/test', function(){
write(path + '/test/app.test.js', appTest);
});
// CSS Engine support
switch (cssEngine) {
case 'sass':
case 'less':
app = app.replace(':CSS', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
break;
default:
app = app.replace(':CSS', '');
}
// Session support
app = app.replace(':SESS', sessions
? '\n app.use(express.cookieDecoder());\n app.use(express.session({ secret: \'your secret here\' }));'
: '');
// Template support
app = app.replace(':TEMPLATE', templateEngine);
write(path + '/app.js', app);
// Suggestions
process.on('exit', function(){
if (cssEngine) {
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
, cssEngine
, cssEngine);
}
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
, templateEngine
, templateEngine);
});
});
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && err.errno !== process.ENOENT) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
*
* @param {String} path
* @param {String} str
*/
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
}
/**
* Prompt confirmation with the given `msg`.
*
* @param {String} msg
* @param {Function} fn
*/
function confirm(msg, fn) {
prompt(msg, function(val){
fn(/^ *y(es)?/i.test(val));
});
}
/**
* Prompt input with the given `msg` and callback `fn`.
*
* @param {String} msg
* @param {Function} fn
*/
function prompt(msg, fn) {
stdin = stdin || process.openStdin();
sys[msg[msg.length - 1] == ' ' ? 'print' : 'puts'](msg);
stdin.setEncoding('ascii');
stdin.addListener('data', function(data){
fn(data);
stdin.removeListener('data', arguments.callee);
});
}
/**
* Mkdir -p.
*
* @param {String} path
* @param {Function} fn
*/
function mkdir(path, fn) {
exec('mkdir -p ' + path, function(err){
if (err) throw err;
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
fn && fn();
});
}
/**
* Exit with the given `str`.
*
* @param {String} str
*/
function abort(str) {
console.error(str);
process.exit(1);
}
Jump to Line
Something went wrong with that request. Please try again.