Hoy vamos con un tutorial de Yeoman para crear un generador de servidor Express adaptado a nuestras necesidades. Para los que no lo sepais, Yeoman es un generador de codigo que nos ayuda a acelerar el proceso de creacion de proyectos
Esta herramienta se puede extender creando generadores propios o usando los generadores que aporta la comunidad. Hoy vamos a crear un generador propio en 10 minutos
Yeoman tutorial
Lo primero es instalar Yeoman en el sistema
npm install -g yo bower grunt-cli gulp
Es necesario instalar tambien Bower, Grunt y Gulp
Una vez instalado todo podemos comenzar a crear nuestro generador. Lo primero es crear el fichero package.json
{ "name": "generator-rubenjgarcia-express", "version": "0.1.0", "description": "Express Generator", "files": [ "app" ], "author": "Ruben J Garcia" }
El nombre tiene que empezar por generator para poder usarlo con Yeoman. Lo siguiente que haremos es instalar la dependencia de la libreria de yeoman-generator
npm install --save yeoman-generator
Vamos a crear nuestro generador principal. Debe estar en el directorio app en el fichero index.js
'use strict'; var generators = require('yeoman-generator'); module.exports = generators.Base.extend({ helloworld: function () { console.log('Hello World!'); } });
Con este codigo ya podremos ejecutar nuestro generador. Usando el comando npm link
y luego yo rubenjgarcia-express
veremos un mensaje en consola como este
Hello World!
Ya tenemos todo configurado y listo para poder continuar. Lo que vamos a hacer es sacar una lista de opciones al usuario para que elija que tipo de servidor quiere, si solo quiere que sea REST con json o que sea un servidor web con capacidad para servir paginas dinamicas, ademas de configurar el puerto, nombre de la aplicacion y si queremos deshabilitar la cabecera X-Powered-By (algo muy recomendable por temas de seguridad)
'use strict'; var generators = require('yeoman-generator'); var slugify = require('underscore.string/slugify'); module.exports = generators.Base.extend({ constructor: function () { generators.Base.apply(this, arguments); //Hacemos que la funcion slugify pueda ser usada en nuestros templates this.slugify = slugify; }, prompting: { //Recogemos todas las opciones appName: function () { var done = this.async(); this.prompt({ type: 'input', name: 'appName', message: 'What is your app name', default: 'My Express App' }, function (response) { this.options.appName = response.appName; done(); }.bind(this)); }, type: function () { var done = this.async(); this.prompt({ type: 'list', name: 'type', message: 'What Express type do you want to create', choices: [ { name: 'REST', value: 'rest' }, { name: 'Web', value: 'web' } ], default: 'rest' }, function (response) { this.options.type = response.type; done(); }.bind(this)); }, disableHeader: function () { var done = this.async(); this.prompt({ type: 'confirm', name: 'disableHeader', message: 'Disable X-Powered-by header', default: true }, function (response) { this.options.disableHeader = response.disableHeader; done(); }.bind(this)); }, port: function () { var done = this.async(); this.prompt({ type: 'input', name: 'port', message: 'What port do you want to listen', default: 3000, validate: function(input) { var done = this.async(); setTimeout(function() { if (isNaN(input) || input <= 1024 || input > 65535) { done("You need to provide a number between 1024 and 65535"); return; } done(true); }, 0); } }, function (response) { this.options.port = response.port; done(); }.bind(this)); } }, writing: function () { var done = this.async(); //De momento solo construimos si es de tipo REST if (this.options.type === 'web') { console.error('Not implemented yet'); done(); } //Generamos los archivos this.template('server.js'); this.template('package.json'); this.template('gulpfile.js'); done(); }, install: function () { //Instalamos las dependencias this.installDependencies({bower: false}); } });
La parte donde se pregunta al usuario las opciones es en el prompting. Si quereis mas informacion sobre como generar las preguntas y los tipos de respuestas que pueden usarse mirad la documentacion de Inquirer
Una vez hecho esto solo queda crear nuestras plantillas. Las plantillas estan basadas en EJS asi que para mas informacion podeis mirar su documentacion. Estas plantillas deben de estar dentro de la carpeta app/templates. Veamos la del servidor: server.js
var logger = require('morgan'); var bodyParser = require('body-parser'); var express = require('express'); var app = express(); <% if (options.disableHeader) { %> app.disable('x-powered-by');<% } %> app.use(bodyParser.json()); app.use(logger('dev')); app.post('/', function (req, res) { res.end(); }); app.use(function (req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); app.use(function (err, req, res, next) { res.status(err.status || 500); if (res.statusCode >= 500 && err.stack) { console.log(err.stack); } res.end(); }); app.set('port', process.env.PORT || <%= options.port %>); var server = app.listen(<%= options.port %>, function () { console.log('Server listening at http://%s:%s', server.address().address, server.address().port) });
Una vez creadas las plantillas ya tenemos todo lo necesario para que funcione nuestro generador. Creamos un directorio nuevo donde vayamos a generar el codigo fuente de nuestro servidor y ejecutamos yo rubenjgarcia-express
y veremos algo asi
? What is your app name My Express App ? What Express type do you want to create REST ? Disable X-Powered-by header Yes ? What port do you want to listen 3000 create package.json create server.js create gulpfile.js I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.
Esto deja todo el codigo generado y las dependencias instaladas y listo para su ejecucion. Como hemos creado un fichero Gulp podemos ejecutar nuestro servidor con el comando gulp
que ademas estara monitorizando los cambios en el codigo fuente y reiniciara automaticamente el despliegue
Podeis descargar el codigo de este proyecto en el repositorio de Github