lunes, 8 de febrero de 2016

Tutorial sobre Bower: Administración de Dependencias Web

https://github.com/bower/bower.github.io/blob/master/img/bower-logo.png

Introducción

Actualmente existen muchos frameworks y librerías disponibles para proyectos web, como por ejemplo AngularJSEmber.jsBackbone.js, Bootstrap, JQuery, entre muchos otros; inclusive se tienen dependencias entre librerías, por ejemplo Bootstrap depende de JQuery.

Cada librería puede incluirse en un proyecto bien sea descargando directamente los archivos necesarios desde su página oficial e incluyéndolos en una carpeta del proyecto o usando referencias hacia un CDN (Content Delivery Network), sin embargo esto hace que se deban incluir y mantener manualmente las dependencias de cada librería.

Bower es una herramienta que permite no solamente descargar librerías para proyectos web sino también tener en cuenta las dependencias que tienen para descargarlas también o advertir que el proyecto tiene una versión de dicha dependencia incompatible. En cierta medida es similar a npm o RubyGems.

Para demostrar su funcionamiento se tomará como base el proyecto desarrollado en el último post introductorio sobre Gulp y se adicionará Bootstrap: https://github.com/guillermo-varela/gulp-demo-watch-live

Nota: en caso de clonar el repositorio desde GitHub, se deben instalar primero las dependencias que ya tiene el proyecto ejecutando:

npm install

Instalación

Bower es un módulo de Node.js, por lo cual se requiere como pre-requisito tener instalado Node.js bien sea con el instalador oficial o mediante Node Version Manager (nvm), como se indica en el post sobre Gulp.

Una vez instalado Node.js se puede instalar Bower globalmente y comprobar la instalación mediante:

npm install -g bower

bower -v
1.7.7

Nota: Para el caso de Bower, no se requiere instalarlo a nivel del proyecto, ya que no se usará su API JavaScript en la construcción del proyecto.

Configuración de Bower

Inicialización

El comando "bower init" permite crear el archivo "bower.json" en el cual se tendrá la configuración de Bower, así como las dependencias usadas. Para ello realizará una serie de preguntas, algunas de las cuales tienen valores por defecto tomadas del archivo "package.json" (si existe) o de la configuración del repositorio Git (si se tiene).

bower init

? name gulp-bower-demo
? description Just a demo project using Bower and Gulp.
? main file
? what types of modules does this package expose?
? keywords bower, bootstrap, gulp
? authors
? license MIT
? homepage https://github.com/guillermo-varela/gulp-bower-demo
? set currently installed components as dependencies? No
? add commonly ignored files to ignore list? No
? would you like to mark this package as private which prevents it from being accidentally published to the registry? Yes

{
  name: 'gulp-bower-demo',
  description: 'Just a demo project using Bower and Gulp.',
  main: '',
  license: 'MIT',
  keywords: [
    'bower',
    'bootstrap',
    'gulp'
  ],
  homepage: 'https://github.com/guillermo-varela/gulp-bower-demo',
  moduleType: [],
  private: true
}

? Looks good? Yes

Al finalizar se debe tener un archivo "bower.json" con el contenido indicado en la confirmación del comando "init".

Carpeta de Instalación de Dependencias

Por defecto las librerías que se instalen como dependencias del proyecto se almacenarán en una carpeta en la raíz del proyecto llamada "bower_components".

Debido a que en este caso el código de la aplicación se encuentra dentro de la carpeta "app" se cambiará la ubicación del directorio de instalación de Bower creando un archivo JSON llamado ".bowerrc" indicando la ruta que se quiere:

{
  "directory": "app/lib"
}

Nota: Para reducir el tamaño ocupado en los repositorios de código, esta carpeta debe ser excluida del sistema de versionamiento.

Administración de Dependencias

Instalando Paquetes

Para instalar una librería como dependencia del proyecto se usa el comando "bower install <paquete>". El paquete a instalar puede ser una URL, un repositorio o un paquete registrado en el sitio oficial de Bower:

# registered package
bower install --save jquery

# GitHub shorthand
bower install --save user/repository

# Git endpoint
bower install --save git://github.com/user/package.git

# URL
bower install --save http://example.com/script.js

El flag "--save" sirve para indicar que adicionalmente a descargar el paquete se quiere también registrar la dependencia en el archivo "bower.json". De esta manera al descargar el proyecto sin las dependencias estas podrán ser descargadas mediante el comando "bower install".

Así al instalar Bootstrap por ejemplo (bower install --save bootstrap) se agrega una nueva entrada en "bower.json" con el nombre "dependencies":

{
  "name": "gulp-bower-demo",
  "description": "Just a demo project using Bower and Gulp.",
  "license": "MIT",
  "keywords": [
    "bower",
    "bootstrap",
    "gulp"
  ],
  "homepage": "https://github.com/guillermo-varela/gulp-bower-demo",
  "moduleType": [],
  "private": true,
  "dependencies": {
    "bootstrap": "^3.3.6"
  }
}

De manera opcional puede indicarse la versión del paquete que se quiere instalar de la siguiente manera: "bower install <paquete>#version". En caso de no indicar la versión Bower instalará la versión más reciente.

Información de los Paquetes

Antes de instalar una dependencia/paquete/librería puede obtenerse información acerca de esta ejecutando "bower info <paquete>" (también se tiene la opción de indicar la versión), por ejemplo para el caso de Bootstrap:

bower info bootstrap

{
  name: 'bootstrap',
  description: 'The most popular front-end framework for developing responsive, mobile first projects on the web.',
  keywords: [
    'css',
    'js',
    'less',
    'mobile-first',
    'responsive',
    'front-end',
    'framework',
    'web'
  ],
  homepage: 'http://getbootstrap.com',
  license: 'MIT',
  moduleType: 'globals',
  main: [
    'less/bootstrap.less',
    'dist/js/bootstrap.js'
  ],
  ignore: [
    '/.*',
    '_config.yml',
    'CNAME',
    'composer.json',
    'CONTRIBUTING.md',
    'docs',
    'js/tests',
    'test-infra'
  ],
  dependencies: {
    jquery: '1.9.1 - 2'
  },
  version: '3.3.6'
}

Available versions:
  - 3.3.6
  - 3.3.5
  - 3.3.4
  - 3.3.2
  - 3.3.1
  - 3.3.0
  - 3.2.0
  - 3.1.1
  - 3.1.0
  - 3.0.3
  - 3.0.2
  - 3.0.1
  - 3.0.0
  - 2.3.2
  - 2.3.1
  - 2.3.0
  - 2.2.2
  - 2.2.1
  - 2.2.0
  - 2.1.1
  - 2.1.0
  - 2.0.4
  - 2.0.3
  - 2.0.2
  - 2.0.1
  - 2.0.0
  - 1.4.0
  - 1.3.0
  - 1.2.0
  - 1.1.1
  - 1.1.0
  - 1.0.0

Show 4 additional prereleases with 'bower info bootstrap --verbose'
You can request info for a specific version with 'bower info bootstrap#'

En la parte resaltada puede verse que para la versión más reciente (3.3.6) se tiene una dependencia con JQuery desde la versión 1.9.1 hasta 2.

Conflicto de Dependencias

Si se intenta instalar Bootstrap pero el proyecto ya está usando una versión distinta de JQuery, Bower preguntará cuál se debe usar:


bower install --save jquery#1.8.9
bower install --save bootstrap

bower bootstrap#*               cached git://github.com/twbs/bootstrap.git#3.3.6
bower bootstrap#*             validate 3.3.6 against git://github.com/twbs/bootstrap.git#*
bower jquery#1.9.1 - 2          cached git://github.com/jquery/jquery-dist.git#2.2.0
bower jquery#1.9.1 - 2        validate 2.2.0 against git://github.com/jquery/jquery-dist.git#1.9.1 - 2

Unable to find a suitable version for jquery, please choose one:
    1) jquery#1.8.0 which resolved to 1.8.0 and is required by gulp-bower-demo
    2) jquery#1.9.1 - 2 which resolved to 2.2.0 and is required by bootstrap#3.3.6

Prefix the choice with ! to persist it to bower.json

? Answer !2
bower jquery                resolution Saved jquery#1.9.1 - 2 as resolution
bower jquery#1.9.1 - 2         install jquery#2.2.0
bower bootstrap#^3.3.6         install bootstrap#3.3.6

jquery#2.2.0 bower_components\jquery

bootstrap#3.3.6 bower_components\bootstrap
└── jquery#2.2.0

En caso de escoger usar la versión compatible con la librería que se quiere instalar, como se hizo en este ejemplo, Bower cambiará la versión que se está usando de la librería anterior por la que necesita para la nueva dependencia en la carpeta de instalación de paquetes, pero en "bower.json" se seguirá indicando que el proyecto necesita la otra versión. Es por esto que lo más recomendable en estos casos es instalar primero la versión de la primera librería que sea compatible con la nueva.

Lista de Paquetes Instalados

El comando "bower list" permite ver los paquetes instalados en el proyecto:

bower list

bower check-new     Checking for new versions of the project dependencies...
gulp-bower-demo /home/user/git/gulp-bower-demo
├─┬ bootstrap#3.3.6 (latest is 4.0.0-alpha.2)
│ └── jquery#2.2.0 (latest is 3.0.0-beta1)
└── jquery#2.2.0 incompatible with 1.8.0 (1.8.0 available, latest is 3.0.0-beta1)

Desinstalar Paquetes

Así como se pueden instalar paquetes, también se pueden desinstalar del proyecto ejecutando "bower uninstall --save <paquete>". Así, para desinstalar JQuery y Bootstrap se debe ejecutar:

bower uninstall --save bootstrap jquery

bower uninstall     bootstrap
bower uninstall     jquery

Otras funcionalidades se pueden encontrar en el API oficial de Bower.

Desarrollo del Proyecto

Como primer paso se debe instalar Bootstrap mediante Bower:

bower install --save bootstrap

Luego se procede a incluir Bootstrap en la página "index.html". Para ese ejemplo se usará como base el ejemplo más sencillo que provee Bootstrap: http://getbootstrap.com/examples/starter-template

app/index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Gulp-Bower Demo">
  <link rel="icon" href="assets/img/favicon.ico">

  <title>Gulp-Bower Demo</title>

  <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css">

  <!-- build:css css/styles.min.css -->
  <!-- inject:css -->
  <link rel="stylesheet" href="css/style1.css">
  <link rel="stylesheet" href="css/style2.css">
  <!-- endinject -->
  <!-- endbuild -->

  <script src="lib/jquery/dist/jquery.min.js"></script>
  <script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>

  <!-- build:js js/scripts.min.js -->
  <!-- inject:js -->
  <script src="js/hello.js"></script>
  <script src="js/printer.js"></script>
  <!-- endinject -->
  <!-- endbuild -->
</head>

<body>
  <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">

      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Gulp-Bower Demo</a>
      </div>

      <div id="navbar" class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
          <li class="active"><a href="#">Home</a></li>
          <li><a href="#about">About</a></li>
          <li><a href="#contact">Contact</a></li>
        </ul>
      </div>

    </div>
  </nav>

  <div class="container">
    <div class="title">
      <h1>Bootstrap-Gulp-Bower starter template</h1>
      <p class="lead">
        Use this document as a way to quickly start any new project.
      </p>
    </div>

    <span id="first" class="text1"></span>
    <br/>
    <span id="second" class="text2"></span>
  </div>

  <script>
    document.getElementById('first').innerHTML = helloWorld();
    document.getElementById('second').innerHTML = printer('Hello World');
  </script>
</body>

</html>

Los cambios fueron:
  • Línea 13: Se incluye la referencia a los estilos de Bootstrap.
  • Líneas 22-23: Se incluyen las referencias a los archivos JavaScript de JQuery y Bootstrap.
  • Líneas 34-56: Para mostrar el funcionamiento de Bootstrap, se copió el menú superior que se tiene en la página oficial de ejemplo.

En este ejemplo las referencias a los archivos JavaScript de JQuery y Bootstrap se están indicando dentro de la etiqueta "head".

En la documentación de Bootstrap y algunos otros sitios puede encontrarse que los archivos JavaScript se referencian justo antes de cerrar la etiqueta "body", lo cual tiene como origen una recomendación de Yahoo para dar la impresión a los usuarios de una carga más rápida de las páginas web, lo cual se ha transformado en una cuestión de gustos personales, ya que se encuentran opiniones a favor y en contra.

Personalmente, para páginas web que no tengan funcionalidades indispensables en JavaScript estas referencias pueden estar al final, ya que la página se puede mostrar al usuario mientras dichos archivos cargan sin perder mayor funcionalidad. Sin embargo para aplicaciones web que sí dependan de JavaScript para su funcionamiento encuentro que es mejor referenciar los archivos JavaScript al principio, ya que cuando la página se le muestre al usuario se tendrá un sitio funcional, en lugar de botones u opciones que no hacen nada. Este razonamiento se ilustra un poco más en el siguiente artículo: http://demianlabs.com/lab/post/top-or-bottom-of-the-page-where-should-you-load-your-javascript


app/css/style1.css
body {
  padding-top: 50px;
}

.title {
  text-align: center;
}

.text1 {
    color: red;
}

Los cambios fueron:
  • Líneas 1-3: Para que el contenido aparezca bajo el nuevo menú superior, se agrega un espacio de 50 píxeles.
  • Líneas 5-7: Se crea una clase para el título para que el texto esté centrado.

Al abrir el archivo "index.html" en un navegador web puede verse que ya se aplican los estilos de Bootstrap y la funcionalidad del menú tanto en navegadores web de PC como en dispositivos móviles.
Figura 1 - Página en navegador web

Figura 2 - Página en navegador web emulando un iPhone

Integración de Bower con Gulp

Inyección de Archivos

Aprovechando que el proyecto ya está usando Gulp, se mostrará cómo se pueden incluir las referencias a los archivos CSS y JavaScript automáticamente usando el plugin "wiredep", el cual inyecta los archivos declarados en la propiedad "main" de cada paquete y se puede instalar con el siguiente comando:

npm install --save-dev wiredep

app/index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Gulp-Bower Demo">
  <link rel="icon" href="assets/img/favicon.ico">

  <title>Gulp-Bower Demo</title>

  <!-- build:css css/vendor.min.css -->
  <!-- bower:css -->
  <!-- endbower -->
  <!-- endbuild -->

  <!-- build:css css/styles.min.css -->
  <!-- inject:css -->
  <link rel="stylesheet" href="css/style1.css">
  <link rel="stylesheet" href="css/style2.css">
  <!-- endinject -->
  <!-- endbuild -->

  <!-- build:js js/vendor.min.js -->
  <!-- bower:js -->
  <!-- endbower -->
  <!-- endbuild -->

  <!-- build:js js/scripts.min.js -->
  <!-- inject:js -->
  <script src="js/hello.js"></script>
  <script src="js/printer.js"></script>
  <!-- endinject -->
  <!-- endbuild -->
</head>

<body>
  <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">

      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Gulp-Bower Demo</a>
      </div>

      <div id="navbar" class="collapse navbar-collapse">
        <ul class="nav navbar-nav">
          <li class="active"><a href="#">Home</a></li>
          <li><a href="#about">About</a></li>
          <li><a href="#contact">Contact</a></li>
        </ul>
      </div>

    </div>
  </nav>

  <div class="container">
    <div class="title">
      <h1>Bootstrap-Gulp-Bower starter template</h1>
      <p class="lead">
        Use this document as a way to quickly start any new project.
      </p>
    </div>

    <span id="first" class="text1"></span>
    <br/>
    <span id="second" class="text2"></span>
  </div>

  <script>
    document.getElementById('first').innerHTML = helloWorld();
    document.getElementById('second').innerHTML = printer('Hello World');
  </script>
</body>

</html>

Para que los archivos de los paquetes instalados con Bower se incluyan en "index.html" se realizaron estos cambios:
  • Líneas 13-16: Se agregó el bloque "bower:css" en el cual "wiredep" inyectará los archivos CSS de los paquetes instalados con Bower. Cabe anotar que este bloque se encuentra dentro del bloque "build:css css/vendor.min.css", ya que los archivos que se inyectarán no están minificados, lo cual permitirá trabajar con los archivos sin comprimir en la fase de desarrollo y al momento de construir la versión de producción (tarea "build" de Gulp) estos archivos estarán también minificados en un archivo referenciado antes que los estilos de la aplicación, para permitir sobrescribir los estilos que se necesiten.
  • Líneas 25-28: Similar al punto anterior, se agregó el bloque "bower:js" en el cual "wiredep" inyectará los archivos JavaScript de los paquetes instalados con Bower. También se tiene un bloque "build:js js/vendor.min.js" por aparte, para realizar la minificación sólo para el ambiente de producción.

gulpfile.js
'use strict';

var gulp        = require('gulp');
var inject      = require('gulp-inject');
var wiredep     = require('wiredep').stream;
var useref      = require('gulp-useref');
var gulpIf      = require('gulp-if');
var uglify      = require('gulp-uglify');
var gutil       = require('gulp-util');
var cssnano     = require('gulp-cssnano');
var jshint      = require('gulp-jshint');
var jscs        = require('gulp-jscs');
var del         = require('del');
var connect     = require('gulp-connect');
var runSequence = require('run-sequence');

// Search for js and css files created for injection in index.html
gulp.task('inject', function () {
  return gulp.src('index.html', {cwd: './app'})
    .pipe(inject(
      gulp.src(['**/*.js', '!./lib/**/*'], {cwd: './app', read: false}), {
        relative: true
      }))
    .pipe(inject(
      gulp.src(['**/*.css', '!./lib/**/*'], {cwd: './app', read: false}), {
        relative: true
      }))
    .pipe(gulp.dest('./app'));
});

// Inject libraries via Bower in between of blocks "bower:xx" in index.html
gulp.task('wiredep', ['inject'], function () {
  return gulp.src('index.html', {cwd: './app'})
    .pipe(wiredep({
      directory: './app/lib/'
    }))
    .pipe(gulp.dest('./app'));
});

// Compress into a single file the ones in between of blocks "build:xx" in index.html
gulp.task('compress', ['wiredep'], function () {
  return gulp.src('index.html', {cwd: './app'})
    .pipe(useref())
    .pipe(gulpIf('**/*.js', uglify({
      mangle: true
    }).on('error', gutil.log)))
    .pipe(gulpIf('**/*.css', cssnano()))
    .pipe(gulp.dest('./dist'));
});

// Copies the assets into the dist folder
gulp.task('copy:assets', function () {
  return gulp.src('assets*/**', {cwd: './app'})
    .pipe(gulp.dest('./dist'));
});

// Looks for code correctness errors in JS and prints them
gulp.task('jshint', function() {
  return gulp.src(['**/*.js', '!./lib/**/*'], {cwd: './app'})
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'))
    .pipe(jshint.reporter('fail'));
});

// Looks for code style errors in JS and prints them
gulp.task('jscs', function () {
  return gulp.src(['**/*.js', '!./lib/**/*'], {cwd: './app'})
    .pipe(jscs())
    .pipe(jscs.reporter())
    .pipe(jscs.reporter('fail'));
});

// Cleans the dist folder
gulp.task('clean:dist', function () {
  return del('dist/**/*');
});

// Watch changes on application files
gulp.task('watch', function() {
  gulp.watch(['**/*.css', '!./lib/**/*'], {cwd: './app'}, ['inject']);
  gulp.watch(['**/*.js', '!./lib/**/*'], {cwd: './app'}, ['jshint', 'jscs', 'inject']);
  gulp.watch(['./bower.json'], ['wiredep']);
  gulp.watch('**/*.html', {cwd: './app'}, function (event) {
    gulp.src(event.path)
      .pipe(connect.reload());
  });
});

// Starts a development web server
gulp.task('server', function () {
  connect.server({
    root: './app',
    hostname: '0.0.0.0',
    port: 8080,
    livereload: true
  });
});

// Starts a server using the production build
gulp.task('server-dist', ['build'], function () {
  connect.server({
    root: './dist',
    hostname: '0.0.0.0',
    port: 8080
  });
});

// Production build
gulp.task('build', function (done) {
  runSequence('jshint', 'jscs', 'clean:dist', 'compress', 'copy:assets', done);
});

gulp.task('default', ['inject', 'wiredep', 'server', 'watch']);

Las modificaciones fueron:
  • Línea 5: Se carga el módulo "wiredep".
  • Línea 21: En lugar de usar una sola expresión regular (glob) para indicar la ruta de los archivos JavaScript a inyectar mediante "inject", se usa un arreglo para además indicar que se deben ignorar los archivos de los paquetes instalados por Bower, que están en "app/lib".
  • Línea 25: De manera similar al punto anterior, se indica que se deben ignorar los archivos de los paquetes de Bower al inyectar los archivos CSS.
  • Líneas 32-38: Se crea la tarea "wiredep" para realizar la inyección de los archivos de los paquetes instalados por Bower en la carpeta "app/lib" en "app/index.html". Debido a que las tareas "inject" y "wiredep" modificarán el contenido del archivo "app/index.html", para evitar condiciones de carrera se hace que "wiredep" dependa de que "inject" termine de ejecutarse.
  • Línea 41: En la tarea que concatena y minifica los archivos (compress) se adiciona como dependencia la nueva tarea "wiredep" para que asegurar que los archivos de los paquetes instalados mediante Bower se encuentran actualizados en "index.html". En este caso ya no es necesario indicar la tarea "inject" como dependencia de "compress" ya que se tiene en "wiredep".
  • Líneas 59 y 67:  Se excluyen del análisis de código JavaScript los archivos de los paquetes instalados mediante Bower.
  • Líneas 80 y 81: De igual manera se excluyen estos archivos en la tarea de monitoreo "watch".
  • Línea 82: Se crea un paso dentro de la tarea "watch" para que cada vez que se modifique el archivo "bower.json" se ejecute la tarea "wiredep" y así actualizar automáticamente los archivos incluidos en "index.html".
  • Línea 113: Se agregan las tareas "inject" y "wiredep" a la tarea por defecto (default) para que al ejecutar "gulp" antes de iniciar el servidor web de desarrollo (tarea "server") se ejecute la inyección de archivos JavaScript, CSS y dependencias Bower.

En este caso se ha optado por añadir las exclusiones de los archivos en "app/lib" para las tareas de análisis de código (jshint y jscs) y monitoreo de cambios (watch), en lugar de simplemente indicar directamente que se quieren los archivos dentro de "app/js" y "app/css" ya que dependiendo del framework y estructura usados se pueden llegar a tener muchas sub-carpetas que contengan dichos tipos de archivos, por ejemplo puede verse la estructura de archivos que propone Google para los proyectos que usan AngularJS: https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub

De esta manera, bien sea ejecutando manualmente "gulp wiredep" o instalando las dependencias mediante Bower mientras se ejecuta la tarea de Gulp "watch" (o la tarea por defecto/default que la incluye) se tienen actualizadas las referencias CSS y JavaScript.

Nota: Desde la versión 3.3.5 de Bootstrap se quitó el archivo "dist/css/bootstrap.cs" de la configuración "main", debido a cambios en la documentación de Bower acerca de este campo. Mientras el equipo que trabaja en Bower revisa este tema, se puede adicionar una configuración que permite adicionar nuevamente este archivo para este proyecto:

bower.json
{
  "name": "gulp-bower-demo",
  "description": "Just a demo project using Bower and Gulp.",
  "license": "MIT",
  "keywords": [
    "bower",
    "bootstrap",
    "gulp"
  ],
  "homepage": "https://github.com/guillermo-varela/gulp-bower-demo",
  "moduleType": [],
  "private": true,
  "dependencies": {
    "bootstrap": "^3.3.6"
  },
  "overrides": {
    "bootstrap": {
      "main": [
        "dist/js/bootstrap.js",
        "dist/css/bootstrap.css",
        "less/bootstrap.less"
      ]
    }
  }
}

Líneas 16-24:  Se indica que para este proyecto se sobrescribirá la propiedad "main" para el paquete "bootstrap" usando los archivos necesarios.

Al ejecutar "gulp wiredep" puede verse que se incluyen los archivos tanto de Bootstrap como de JQuery:

index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="description" content="Gulp-Bower Demo">
  <link rel="icon" href="assets/img/favicon.ico">

  <title>Gulp-Bower Demo</title>

  <!-- build:css css/vendor.min.css -->
  <!-- bower:css -->
  <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css" />
  <!-- endbower -->
  <!-- endbuild -->

  <!-- build:css css/styles.min.css -->
  <!-- inject:css -->
  <link rel="stylesheet" href="css/style1.css">
  <link rel="stylesheet" href="css/style2.css">
  <!-- endinject -->
  <!-- endbuild -->

  <!-- build:js js/vendor.min.js -->
  <!-- bower:js -->
  <script src="lib/jquery/dist/jquery.js"></script>
  <script src="lib/bootstrap/dist/js/bootstrap.js"></script>
  <!-- endbower -->
  <!-- endbuild -->

  <!-- build:js js/scripts.min.js -->
  <!-- inject:js -->
  <script src="js/hello.js"></script>
  <script src="js/printer.js"></script>
  <!-- endinject -->
  <!-- endbuild -->
</head>
...

Construcción para Producción

Dado que la tarea "wiredep" ya fue incluida como dependencia de "compress", basta con iniciar la construcción de los archivos para producción para que los archivos de los paquetes instalados mediante Bower sean tenidos en cuenta:

gulp build

Using gulpfile /home/user/git/gulp-bower-demo/gulpfile.js
Starting 'build'...
Starting 'jshint'...
Finished 'jshint' after 110 ms
Starting 'jscs'...
Finished 'jscs' after 248 ms
Starting 'clean:dist'...
Finished 'clean:dist' after 13 ms
Starting 'inject'...
Starting 'wiredep'...
Finished 'wiredep' after 2.4 ms
gulp-inject 2 files into index.html.
gulp-inject 2 files into index.html.
Finished 'inject' after 89 ms
Starting 'compress'...
Finished 'compress' after 3.34 s
Starting 'copy:assets'...
Finished 'copy:assets' after 6.51 ms
Finished 'build' after 3.83 s

El resultado en "dist" debe ser:
dist
|   index.html
|
+---assets
|   \---img
|           favicon.ico
|           globe.png
|
+---css
|       styles.min.css
|       vendor.min.css
|
\---js
        scripts.min.js
        vendor.min.js

Ejecutando "gulp" o "gulp server-dist" puede comprobarse que la página funciona perfectamente con el servidor local usando el LiveReload o los archivos de la construcción para producción respectivamente.
Figura 3 - Página desde el servidor local usando archivos de desarrollo "gulp"

Figura 4 - Página desde el servidor local usando archivos de producción "gulp build server-dist"


El proyecto completo se puede descargar desde: https://github.com/guillermo-varela/gulp-bower-demo

Conclusiones

Conociendo de una manera relativamente claro qué es Bower, cómo funciona y cómo se puede integrar con Gulp se puede llegar a tener un ambiente de trabajo para proyectos web más completo, en cuanto que ahora no sólo se tienen tareas automatizadas y análisis de código, sino también administración de dependencias web, lo cual aumenta la probabilidad de encontrar y solucionar problemas antes de desplegar las aplicaciones web en producción.

Referencias

No hay comentarios.:

Publicar un comentario