3.10.0
[express.git] / bin / express
blobad0d64a810d18ebfbb8947a3eb37264023eef2f0
1 #!/usr/bin/env node
3 /**
4  * Module dependencies.
5  */
7 var program = require('commander')
8   , mkdirp = require('mkdirp')
9   , pkg = require('../package.json')
10   , version = pkg.version
11   , os = require('os')
12   , fs = require('fs');
14 // CLI
16 program
17   .version(version)
18   .usage('[options] [dir]')
19   .option('-s, --sessions', 'add session support')
20   .option('-e, --ejs', 'add ejs engine support (defaults to jade)')
21   .option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
22   .option('-H, --hogan', 'add hogan.js engine support')
23   .option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
24   .option('-f, --force', 'force on non-empty directory')
25   .parse(process.argv);
27 // Path
29 var path = program.args.shift() || '.';
31 // end-of-line code
33 var eol = os.EOL
35 // Template engine
37 program.template = 'jade';
38 if (program.ejs) program.template = 'ejs';
39 if (program.jshtml) program.template = 'jshtml';
40 if (program.hogan) program.template = 'hjs';
42 /**
43  * Routes index template.
44  */
46 var index = [
47     ''
48   , '/*'
49   , ' * GET home page.'
50   , ' */'
51   , ''
52   , 'exports.index = function(req, res){'
53   , '  res.render(\'index\', { title: \'Express\' });'
54   , '};'
55 ].join(eol);
57 /**
58  * Routes users template.
59  */
61 var users = [
62     ''
63   , '/*'
64   , ' * GET users listing.'
65   , ' */'
66   , ''
67   , 'exports.list = function(req, res){'
68   , '  res.send("respond with a resource");'
69   , '};'
70 ].join(eol);
72 /**
73  * Jade layout template.
74  */
76 var jadeLayout = [
77     'doctype html'
78   , 'html'
79   , '  head'
80   , '    title= title'
81   , '    link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
82   , '  body'
83   , '    block content'
84 ].join(eol);
86 /**
87  * Jade index template.
88  */
90 var jadeIndex = [
91     'extends layout'
92   , ''
93   , 'block content'
94   , '  h1= title'
95   , '  p Welcome to #{title}'
96 ].join(eol);
98 /**
99  * EJS index template.
100  */
102 var ejsIndex = [
103     '<!DOCTYPE html>'
104   , '<html>'
105   , '  <head>'
106   , '    <title><%= title %></title>'
107   , '    <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
108   , '  </head>'
109   , '  <body>'
110   , '    <h1><%= title %></h1>'
111   , '    <p>Welcome to <%= title %></p>'
112   , '  </body>'
113   , '</html>'
114 ].join(eol);
117  * JSHTML layout template.
118  */
120 var jshtmlLayout = [
121     '<!DOCTYPE html>'
122   , '<html>'
123   , '  <head>'
124   , '    <title> @write(title) </title>'
125   , '    <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
126   , '  </head>'
127   , '  <body>'
128   , '    @write(body)'
129   , '  </body>'
130   , '</html>'
131 ].join(eol);
134  * JSHTML index template.
135  */
137 var jshtmlIndex = [
138     '<h1>@write(title)</h1>'
139   , '<p>Welcome to @write(title)</p>'
140 ].join(eol);
143  * Hogan.js index template.
144  */
145 var hoganIndex = [
146     '<!DOCTYPE html>'
147   , '<html>'
148   , '  <head>'
149   , '    <title>{{ title }}</title>'
150   , '    <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
151   , '  </head>'
152   , '  <body>'
153   , '    <h1>{{ title }}</h1>'
154   , '    <p>Welcome to {{ title }}</p>'
155   , '  </body>'
156   , '</html>'
157 ].join(eol);
160  * Default css template.
161  */
163 var css = [
164     'body {'
165   , '  padding: 50px;'
166   , '  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
167   , '}'
168   , ''
169   , 'a {'
170   , '  color: #00B7FF;'
171   , '}'
172 ].join(eol);
175  * Default less template.
176  */
178 var less = [
179     'body {'
180   , '  padding: 50px;'
181   , '  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
182   , '}'
183   , ''
184   , 'a {'
185   , '  color: #00B7FF;'
186   , '}'
187 ].join(eol);
190  * Default stylus template.
191  */
193 var stylus = [
194     'body'
195   , '  padding: 50px'
196   , '  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
197   , 'a'
198   , '  color: #00B7FF'
199 ].join(eol);
202  * App template.
203  */
205 var app = [
206     ''
207   , '/**'
208   , ' * Module dependencies.'
209   , ' */'
210   , ''
211   , 'var express = require(\'express\');'
212   , 'var routes = require(\'./routes\');'
213   , 'var user = require(\'./routes/user\');'
214   , 'var http = require(\'http\');'
215   , 'var path = require(\'path\');'
216   , ''
217   , 'var app = express();'
218   , ''
219   , '// all environments'
220   , 'app.set(\'port\', process.env.PORT || 3000);'
221   , 'app.set(\'views\', path.join(__dirname, \'views\'));'
222   , 'app.set(\'view engine\', \':TEMPLATE\');'
223   , 'app.use(express.favicon());'
224   , 'app.use(express.logger(\'dev\'));'
225   , 'app.use(express.json());'
226   , 'app.use(express.urlencoded());'
227   , 'app.use(express.methodOverride());{sess}'
228   , 'app.use(app.router);{css}'
229   , 'app.use(express.static(path.join(__dirname, \'public\')));'
230   , ''
231   , '// development only'
232   , 'if (\'development\' == app.get(\'env\')) {'
233   , '  app.use(express.errorHandler());'
234   , '}'
235   , ''
236   , 'app.get(\'/\', routes.index);'
237   , 'app.get(\'/users\', user.list);'
238   , ''
239   , 'http.createServer(app).listen(app.get(\'port\'), function(){'
240   , '  console.log(\'Express server listening on port \' + app.get(\'port\'));'
241   , '});'
242   , ''
243 ].join(eol);
245 // Generate application
247 (function createApplication(path) {
248   emptyDirectory(path, function(empty){
249     if (empty || program.force) {
250       createApplicationAt(path);
251     } else {
252       program.confirm('destination is not empty, continue? ', function(ok){
253         if (ok) {
254           process.stdin.destroy();
255           createApplicationAt(path);
256         } else {
257           abort('aborting');
258         }
259       });
260     }
261   });
262 })(path);
265  * Create application at the given directory `path`.
267  * @param {String} path
268  */
270 function createApplicationAt(path) {
271   console.log();
272   process.on('exit', function(){
273     console.log();
274     console.log('   install dependencies:');
275     console.log('     $ cd %s && npm install', path);
276     console.log();
277     console.log('   run the app:');
278     console.log('     $ node app');
279     console.log();
280   });
282   mkdir(path, function(){
283     mkdir(path + '/public');
284     mkdir(path + '/public/javascripts');
285     mkdir(path + '/public/images');
286     mkdir(path + '/public/stylesheets', function(){
287       switch (program.css) {
288         case 'less':
289           write(path + '/public/stylesheets/style.less', less);
290           break;
291         case 'stylus':
292           write(path + '/public/stylesheets/style.styl', stylus);
293           break;
294         default:
295           write(path + '/public/stylesheets/style.css', css);
296       }
297     });
299     mkdir(path + '/routes', function(){
300       write(path + '/routes/index.js', index);
301       write(path + '/routes/user.js', users);
302     });
304     mkdir(path + '/views', function(){
305       switch (program.template) {
306         case 'ejs':
307           write(path + '/views/index.ejs', ejsIndex);
308           break;
309         case 'jade':
310           write(path + '/views/layout.jade', jadeLayout);
311           write(path + '/views/index.jade', jadeIndex);
312           break;
313         case 'jshtml':
314           write(path + '/views/layout.jshtml', jshtmlLayout);
315           write(path + '/views/index.jshtml', jshtmlIndex);
316           break;
317         case 'hjs':
318           write(path + '/views/index.hjs', hoganIndex);
319           break;
321       }
322     });
324     // CSS Engine support
325     switch (program.css) {
326       case 'less':
327         app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
328         break;
329       case 'stylus':
330         app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
331         break;
332       default:
333         app = app.replace('{css}', '');
334     }
336     // Session support
337     app = app.replace('{sess}', program.sessions
338       ? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
339       : '');
341     // Template support
342     app = app.replace(':TEMPLATE', program.template);
344     // package.json
345     var pkg = {
346         name: 'application-name'
347       , version: '0.0.1'
348       , private: true
349       , scripts: { start: 'node app.js' }
350       , dependencies: {
351         express: version
352       }
353     }
355     if (program.template) pkg.dependencies[program.template] = '*';
357     // CSS Engine support
358     switch (program.css) {
359       case 'less':
360         pkg.dependencies['less-middleware'] = '~0.1.15';
361         break;
362       default:
363         if (program.css) {
364           pkg.dependencies[program.css] = '*';
365         }
366     }
368     write(path + '/package.json', JSON.stringify(pkg, null, 2));
369     write(path + '/app.js', app);
370   });
374  * Check if the given directory `path` is empty.
376  * @param {String} path
377  * @param {Function} fn
378  */
380 function emptyDirectory(path, fn) {
381   fs.readdir(path, function(err, files){
382     if (err && 'ENOENT' != err.code) throw err;
383     fn(!files || !files.length);
384   });
388  * echo str > path.
390  * @param {String} path
391  * @param {String} str
392  */
394 function write(path, str) {
395   fs.writeFile(path, str);
396   console.log('   \x1b[36mcreate\x1b[0m : ' + path);
400  * Mkdir -p.
402  * @param {String} path
403  * @param {Function} fn
404  */
406 function mkdir(path, fn) {
407   mkdirp(path, 0755, function(err){
408     if (err) throw err;
409     console.log('   \033[36mcreate\033[0m : ' + path);
410     fn && fn();
411   });
415  * Exit with the given `str`.
417  * @param {String} str
418  */
420 function abort(str) {
421   console.error(str);
422   process.exit(1);