--- /dev/null
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs')
+ , exec = require('child_process').exec;
+
+/**
+ * Framework version.
+ */
+
+var version = '2.3.3';
+
+/**
+ * Add session support.
+ */
+
+var sessions = false;
+
+/**
+ * CSS engine to utilize.
+ */
+
+var cssEngine;
+
+/**
+ * Template engine to utilize.
+ */
+
+var templateEngine = 'jade';
+
+/**
+ * Usage documentation.
+ */
+
+var usage = ''
+ + '\n'
+ + ' Usage: express [options] [path]\n'
+ + '\n'
+ + ' Options:\n'
+ + ' -s, --sessions add session support\n'
+ + ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
+ + ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=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;'
+ , '}'
+ , ''
+ , 'a {'
+ , ' color: #00B7FF;'
+ , '}'
+].join('\n');
+
+/**
+ * Default less template.
+ */
+
+var less = [
+ 'body {'
+ , ' padding: 50px;'
+ , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
+ , '}'
+ , ''
+ , 'a {'
+ , ' color: #00B7FF;'
+ , '}'
+].join('\n');
+
+/**
+ * Default sass template.
+ */
+
+var sass = [
+ 'body'
+ , ' :padding 50px'
+ , ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
+ , 'a'
+ , ' :color #00B7FF'
+].join('\n');
+
+/**
+ * Default stylus template.
+ */
+
+var stylus = [
+ 'body'
+ , ' padding 50px'
+ , ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
+ , 'a'
+ , ' color #00B7FF'
+].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.bodyParser());'
+ , ' app.use(express.methodOverride());{sess}{css}'
+ , ' app.use(app.router);'
+ , ' app.use(express.static(__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\', {'
+ , ' 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) {
+ process.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 'stylus':
+ write(path + '/public/stylesheets/style.styl', stylus);
+ break;
+ 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', 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;
+ case 'stylus':
+ app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
+ break;
+ default:
+ app = app.replace('{css}', '');
+ }
+
+ // Session support
+ app = app.replace('{sess}', sessions
+ ? '\n app.use(express.cookieParser());\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 && 'ENOENT' != err.code) 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[36mcreate\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) {
+ // prompt
+ if (' ' == msg[msg.length - 1]) {
+ process.stdout.write(msg);
+ } else {
+ console.log(msg);
+ }
+
+ // stdin
+ process.stdin.setEncoding('ascii');
+ process.stdin.once('data', function(data){
+ fn(data);
+ }).resume();
+}
+
+/**
+ * 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[36mcreate\x1b[0m : ' + path);
+ fn && fn();
+ });
+}
+
+/**
+ * Exit with the given `str`.
+ *
+ * @param {String} str
+ */
+
+function abort(str) {
+ console.error(str);
+ process.exit(1);
+}