| #!/usr/bin/env node |
| |
| /** |
| * Module dependencies. |
| */ |
| |
| var fs = require('fs') |
| , os = require('os') |
| , exec = require('child_process').exec |
| , mkdirp = require('mkdirp'); |
| |
| /** |
| * Framework version. |
| */ |
| |
| var version = '2.5.7'; |
| |
| /** |
| * Add session support. |
| */ |
| |
| var sessions = false; |
| |
| /** |
| * CSS engine to utilize. |
| */ |
| |
| var cssEngine; |
| |
| /** |
| * End-of-line code. |
| */ |
| |
| var eol = os.platform |
| ? ('win32' == os.platform() ? '\r\n' : '\n') |
| : '\n'; |
| |
| /** |
| * 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 (stylus). default=plain css\n' |
| + ' -v, --version output framework version\n' |
| + ' -h, --help output help information\n' |
| ; |
| |
| /** |
| * Routes index template. |
| */ |
| |
| var index = [ |
| '' |
| , '/*' |
| , ' * GET home page.' |
| , ' */' |
| , '' |
| , 'exports.index = function(req, res){' |
| , ' res.render(\'index\', { title: \'Express\' })' |
| , '};' |
| ].join(eol); |
| |
| /** |
| * Jade layout template. |
| */ |
| |
| var jadeLayout = [ |
| '!!!' |
| , 'html' |
| , ' head' |
| , ' title= title' |
| , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')' |
| , ' body!= body' |
| ].join(eol); |
| |
| /** |
| * Jade index template. |
| */ |
| |
| var jadeIndex = [ |
| 'h1= title' |
| , 'p Welcome to #{title}' |
| ].join(eol); |
| |
| /** |
| * 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(eol); |
| |
| /** |
| * EJS index template. |
| */ |
| |
| var ejsIndex = [ |
| '<h1><%= title %></h1>' |
| , '<p>Welcome to <%= title %></p>' |
| ].join(eol); |
| |
| /** |
| * Default css template. |
| */ |
| |
| var css = [ |
| 'body {' |
| , ' padding: 50px;' |
| , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' |
| , '}' |
| , '' |
| , 'a {' |
| , ' color: #00B7FF;' |
| , '}' |
| ].join(eol); |
| |
| /** |
| * Default stylus template. |
| */ |
| |
| var stylus = [ |
| 'body' |
| , ' padding: 50px' |
| , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif' |
| , 'a' |
| , ' color: #00B7FF' |
| ].join(eol); |
| |
| /** |
| * App template. |
| */ |
| |
| var app = [ |
| '' |
| , '/**' |
| , ' * Module dependencies.' |
| , ' */' |
| , '' |
| , 'var express = require(\'express\')' |
| , ' , routes = require(\'./routes\');' |
| , '' |
| , '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(\'/\', routes.index);' |
| , '' |
| , 'app.listen(3000);' |
| , 'console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);' |
| , '' |
| ].join(eol); |
| |
| // 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) { |
| console.log(); |
| process.on('exit', function(){ |
| console.log(); |
| console.log(' dont forget to install dependencies:'); |
| console.log(' $ cd %s && npm install', path); |
| console.log(); |
| }); |
| |
| mkdir(path, function(){ |
| mkdir(path + '/public'); |
| 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; |
| default: |
| write(path + '/public/stylesheets/style.css', css); |
| } |
| }); |
| |
| mkdir(path + '/routes', function(){ |
| write(path + '/routes/index.js', index); |
| }); |
| |
| 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; |
| } |
| }); |
| |
| // CSS Engine support |
| switch (cssEngine) { |
| case 'stylus': |
| app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));'); |
| break; |
| default: |
| app = app.replace('{css}', ''); |
| } |
| |
| // Session support |
| app = app.replace('{sess}', sessions |
| ? eol + ' app.use(express.cookieParser());' + eol + ' app.use(express.session({ secret: \'your secret here\' }));' |
| : ''); |
| |
| // Template support |
| app = app.replace(':TEMPLATE', templateEngine); |
| |
| // package.json |
| var json = '{' + eol; |
| json += ' "name": "application-name"' + eol; |
| json += ' , "version": "0.0.1"' + eol; |
| json += ' , "private": true' + eol; |
| json += ' , "dependencies": {' + eol; |
| json += ' "express": "' + version + '"' + eol; |
| if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"' + eol; |
| if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"' + eol; |
| json += ' }' + eol; |
| json += '}'; |
| |
| |
| write(path + '/package.json', json); |
| write(path + '/app.js', app); |
| }); |
| } |
| |
| /** |
| * 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) { |
| mkdirp(path, 0755, function(err){ |
| if (err) throw err; |
| console.log(' \033[36mcreate\033[0m : ' + path); |
| fn && fn(); |
| }); |
| } |
| |
| /** |
| * Exit with the given `str`. |
| * |
| * @param {String} str |
| */ |
| |
| function abort(str) { |
| console.error(str); |
| process.exit(1); |
| } |