express 8.9 KB

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