#!/usr/bin/env node /** * Module dependencies. */ var program = require('commander') , mkdirp = require('mkdirp') , pkg = require('../package.json') , version = pkg.version , os = require('os') , fs = require('fs'); // CLI program .version(version) .usage('[options] [dir]') .option('-s, --sessions', 'add session support') .option('-e, --ejs', 'add ejs engine support (defaults to jade)') .option('-J, --jshtml', 'add jshtml engine support (defaults to jade)') .option('-H, --hogan', 'add hogan.js engine support') .option('-c, --css ', 'add stylesheet support (less|stylus) (defaults to plain css)') .option('-f, --force', 'force on non-empty directory') .parse(process.argv); // Path var path = program.args.shift() || '.'; // end-of-line code var eol = os.EOL // Template engine program.template = 'jade'; if (program.ejs) program.template = 'ejs'; if (program.jshtml) program.template = 'jshtml'; if (program.hogan) program.template = 'hjs'; /** * Routes index template. */ var index = [ '' , '/*' , ' * GET home page.' , ' */' , '' , 'exports.index = function(req, res){' , ' res.render(\'index\', { title: \'Express\' });' , '};' ].join(eol); /** * Routes users template. */ var users = [ '' , '/*' , ' * GET users listing.' , ' */' , '' , 'exports.list = function(req, res){' , ' res.send("respond with a resource");' , '};' ].join(eol); /** * Jade layout template. */ var jadeLayout = [ 'doctype html' , 'html' , ' head' , ' title= title' , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')' , ' body' , ' block content' ].join(eol); /** * Jade index template. */ var jadeIndex = [ 'extends layout' , '' , 'block content' , ' h1= title' , ' p Welcome to #{title}' ].join(eol); /** * EJS index template. */ var ejsIndex = [ '' , '' , ' ' , ' <%= title %>' , ' ' , ' ' , ' ' , '

<%= title %>

' , '

Welcome to <%= title %>

' , ' ' , '' ].join(eol); /** * JSHTML layout template. */ var jshtmlLayout = [ '' , '' , ' ' , ' @write(title) ' , ' ' , ' ' , ' ' , ' @write(body)' , ' ' , '' ].join(eol); /** * JSHTML index template. */ var jshtmlIndex = [ '

@write(title)

' , '

Welcome to @write(title)

' ].join(eol); /** * Hogan.js index template. */ var hoganIndex = [ '' , '' , ' ' , ' {{ title }}' , ' ' , ' ' , ' ' , '

{{ title }}

' , '

Welcome to {{ title }}

' , ' ' , '' ].join(eol); /** * Default css template. */ var css = [ 'body {' , ' padding: 50px;' , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' , '}' , '' , 'a {' , ' color: #00B7FF;' , '}' ].join(eol); /** * Default less template. */ var less = [ '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\');' , 'var routes = require(\'./routes\');' , 'var user = require(\'./routes/user\');' , 'var http = require(\'http\');' , 'var path = require(\'path\');' , '' , 'var app = express();' , '' , '// all environments' , 'app.set(\'port\', process.env.PORT || 3000);' , 'app.set(\'views\', path.join(__dirname, \'views\'));' , 'app.set(\'view engine\', \':TEMPLATE\');' , 'app.use(express.favicon());' , 'app.use(express.logger(\'dev\'));' , 'app.use(express.json());' , 'app.use(express.urlencoded());' , 'app.use(express.methodOverride());{sess}' , 'app.use(app.router);{css}' , 'app.use(express.static(path.join(__dirname, \'public\')));' , '' , '// development only' , 'if (\'development\' == app.get(\'env\')) {' , ' app.use(express.errorHandler());' , '}' , '' , 'app.get(\'/\', routes.index);' , 'app.get(\'/users\', user.list);' , '' , 'http.createServer(app).listen(app.get(\'port\'), function(){' , ' console.log(\'Express server listening on port \' + app.get(\'port\'));' , '});' , '' ].join(eol); // Generate application (function createApplication(path) { emptyDirectory(path, function(empty){ if (empty || program.force) { createApplicationAt(path); } else { program.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(' install dependencies:'); console.log(' $ cd %s && npm install', path); console.log(); console.log(' run the app:'); console.log(' $ node app'); console.log(); }); mkdir(path, function(){ mkdir(path + '/public'); mkdir(path + '/public/javascripts'); mkdir(path + '/public/images'); mkdir(path + '/public/stylesheets', function(){ switch (program.css) { case 'less': write(path + '/public/stylesheets/style.less', less); break; 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); write(path + '/routes/user.js', users); }); mkdir(path + '/views', function(){ switch (program.template) { case 'ejs': write(path + '/views/index.ejs', ejsIndex); break; case 'jade': write(path + '/views/layout.jade', jadeLayout); write(path + '/views/index.jade', jadeIndex); break; case 'jshtml': write(path + '/views/layout.jshtml', jshtmlLayout); write(path + '/views/index.jshtml', jshtmlIndex); break; case 'hjs': write(path + '/views/index.hjs', hoganIndex); break; } }); // CSS Engine support switch (program.css) { case 'less': app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));'); break; case 'stylus': app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));'); break; default: app = app.replace('{css}', ''); } // Session support app = app.replace('{sess}', program.sessions ? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());' : ''); // Template support app = app.replace(':TEMPLATE', program.template); // package.json var pkg = { name: 'application-name' , version: '0.0.1' , private: true , scripts: { start: 'node app.js' } , dependencies: { express: version } } if (program.template) pkg.dependencies[program.template] = '*'; // CSS Engine support switch (program.css) { case 'less': pkg.dependencies['less-middleware'] = '*'; break; default: if (program.css) { pkg.dependencies[program.css] = '*'; } } write(path + '/package.json', JSON.stringify(pkg, null, 2)); 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); } /** * 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); }