diff --git a/README.md b/README.md index ca18976e..062b6ebf 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ This generator can also be further configured with the following command line fl -H, --hogan add hogan.js engine support -v, --view add view support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) --no-view use static html instead of view engine + -a, --api use web api template without view engine -c, --css add stylesheet support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory diff --git a/bin/express-cli.js b/bin/express-cli.js index d0c80b7f..c1bd7e31 100755 --- a/bin/express-cli.js +++ b/bin/express-cli.js @@ -54,6 +54,7 @@ program .option('-H, --hogan', 'add hogan.js engine support', renamedOption('--hogan', '--view=hogan')) .option('-v, --view ', 'add view support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)') .option(' --no-view', 'use static html instead of view engine') + .option('-a, --api', 'use web api template without view engine') .option('-c, --css ', 'add stylesheet support (less|stylus|compass|sass) (defaults to plain css)') .option(' --git', 'add .gitignore') .option('-f, --force', 'force on non-empty directory') @@ -207,7 +208,11 @@ function createApplication (name, dir) { // copy route templates mkdir(dir, 'routes') - copyTemplateMulti('js/routes', dir + '/routes', '*.js') + if (!program.api) { + copyTemplateMulti('js/routes', dir + '/routes', '*.js') + } else { + copyTemplateMulti('js/routes/api', dir + '/routes', '*.js') + } if (program.view) { // Copy view templates @@ -239,6 +244,9 @@ function createApplication (name, dir) { copyTemplateMulti('views', dir + '/views', '*.vash') break } + } else if (program.api) { + // Add http-errors dependencies + pkg.dependencies['http-errors'] = '~1.6.2' } else { // Copy extra public files copyTemplate('js/index.html', path.join(dir, 'public/index.html')) @@ -268,13 +276,19 @@ function createApplication (name, dir) { break } - // Index router mount - app.locals.localModules.indexRouter = './routes/index' - app.locals.mounts.push({ path: '/', code: 'indexRouter' }) + if (!program.api) { + // Index router mount + app.locals.localModules.indexRouter = './routes/index' + app.locals.mounts.push({ path: '/', code: 'indexRouter' }) - // User router mount - app.locals.localModules.usersRouter = './routes/users' - app.locals.mounts.push({ path: '/users', code: 'usersRouter' }) + // User router mount + app.locals.localModules.usersRouter = './routes/users' + app.locals.mounts.push({ path: '/users', code: 'usersRouter' }) + } else { + // Value router mount + app.locals.localModules.apiRouter = './routes/values' + app.locals.mounts.push({ path: '/api', code: 'apiRouter' }) + } // Template support switch (program.view) { @@ -319,6 +333,8 @@ function createApplication (name, dir) { break } + app.locals.api = program.api + // Static files app.locals.uses.push("express.static(path.join(__dirname, 'public'))") @@ -460,10 +476,12 @@ function main () { } // Default view engine - if (program.view === true) { + if (program.view === true && !program.api) { warning('the default view engine will not be jade in future releases\n' + "use `--view=jade' or `--help' for additional options") program.view = 'jade' + } else if (program.api) { + program.view = false } // Generate application diff --git a/templates/js/app.js.ejs b/templates/js/app.js.ejs index 2ef75aec..27ef199d 100644 --- a/templates/js/app.js.ejs +++ b/templates/js/app.js.ejs @@ -1,4 +1,4 @@ -<% if (view) { -%> +<% if (view || api) { -%> var createError = require('http-errors'); <% } -%> var express = require('express'); @@ -30,7 +30,7 @@ app.use(<%- use %>); app.use(<%= mount.path %>, <%- mount.code %>); <% }); -%> -<% if (view) { -%> +<% if (view || api) { -%> // catch 404 and forward to error handler app.use(function(req, res, next) { next(createError(404)); @@ -44,7 +44,12 @@ app.use(function(err, req, res, next) { // render the error page res.status(err.status || 500); + <% if (view) { -%> res.render('error'); + <% } -%> + <% if (api) { -%> + res.json({ 'message' : err.message }); + <% } -%> }); <% } -%> diff --git a/templates/js/routes/api/values.js b/templates/js/routes/api/values.js new file mode 100644 index 00000000..f3652e0e --- /dev/null +++ b/templates/js/routes/api/values.js @@ -0,0 +1,29 @@ +var express = require('express'); +var router = express.Router(); + +/* GET values */ +router.get('/values', function(req, res, next) { + res.json(['value1', 'value2']); +}); + +/* GET values/5 */ +router.get('/values/:id', function(req, res, next) { + res.send('value'); +}); + +/* POST values */ +router.post('/values', function(req, res, next) { + res.send(); +}); + +/* PUT values/5 */ +router.put('/values/:id', function(req, res, next) { + res.send(); +}); + +/* DELETE values/5 */ +router.delete('/values/:id', function(req, res, next) { + res.send(); +}); + +module.exports = router; \ No newline at end of file diff --git a/test/cmd.js b/test/cmd.js index c0711a73..a80776a3 100644 --- a/test/cmd.js +++ b/test/cmd.js @@ -633,6 +633,86 @@ describe('express(1)', function () { }) }) + describe('--api', function () { + var ctx = setupTestEnvironment(this.fullTitle()) + + it('should create web api app without view engine', function (done) { + run(ctx.dir, ['--api'], function (err, stdout) { + if (err) return done(err) + ctx.files = utils.parseCreatedFiles(stdout, ctx.dir) + assert.strictEqual(ctx.files.length, 11) + done() + }) + }) + + it('should have basic files', function () { + assert.notStrictEqual(ctx.files.indexOf('bin/www'), -1) + assert.notStrictEqual(ctx.files.indexOf('app.js'), -1) + assert.notStrictEqual(ctx.files.indexOf('package.json'), -1) + }) + + it('should not have views directory', function () { + assert.strictEqual(ctx.files.indexOf('views'), -1) + }) + + it('should have installable dependencies', function (done) { + this.timeout(NPM_INSTALL_TIMEOUT) + npmInstall(ctx.dir, done) + }) + + describe('npm start', function () { + before('start app', function () { + this.app = new AppRunner(ctx.dir) + }) + + after('stop app', function (done) { + this.timeout(APP_START_STOP_TIMEOUT) + this.app.stop(done) + }) + + it('should start app', function (done) { + this.timeout(APP_START_STOP_TIMEOUT) + this.app.start(done) + }) + + it('should respond to HTTP GET request', function (done) { + request(this.app) + .get('/api/values') + .expect(200, ['value1', 'value2'], done) + }) + + it('should respond to HTTP GET request with id', function (done) { + request(this.app) + .get('/api/values/5') + .expect(200, 'value', done) + }) + + it('should respond to HTTP POST request', function (done) { + request(this.app) + .post('/api/values') + .expect(200, '', done) + }) + + it('should respond to HTTP PUT request with id', function (done) { + request(this.app) + .put('/api/values/5') + .expect(200, '', done) + }) + + it('should respond to HTTP DELETE request with id', function (done) { + request(this.app) + .delete('/api/values/5') + .expect(200, '', done) + }) + + it('should generate a 404', function (done) { + request(this.app) + .get('/api/does_not_exist') + .expect(404, { 'message': 'Not Found' }, done) + }) + }) + }) + describe('--pug', function () { var ctx = setupTestEnvironment(this.fullTitle())