├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── gulpfile.js ├── index.html ├── keynotes ├── gulp.pdf └── http2.pdf ├── package.json └── src ├── css ├── app.scss ├── elements.scss ├── layout.scss └── vars.scss └── js ├── dom.js └── functions.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled binary addons (http://nodejs.org/api/addons.html) 2 | dist/ 3 | 4 | # Dependency directories 5 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 luisδμ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ejemplo de iniciación a Gulp para Adalab 2 | 3 | Podéis ver la presentación que utilicé en el taller [aquí](keynotes/gulp.pdf), en formato PDF. 4 | 5 | ## Ejecución 6 | 7 | Para usar este ejemplo hay que seguir los siguientes pasos: 8 | - Instalar Node y npm desde su [web oficial](https://nodejs.org/en/) (Windows, MacOS). También se puede hacer mediante consola ejecutando `apt-get install nodejs` (en Ubuntu o Debian) o usando [Homebrew](https://brew.sh/) (en MacOS). 9 | 10 | - Instalar todas las dependencias de npm listadas en *package.json* ejecutando `npm install`. 11 | 12 | - Instalar Gulp también de forma global ejecutando `npm -g install gulp` (puede requerir permisos de superusuario, en cuyo caso es necesario poner `sudo` delante). De este modo se podrá usar como comando de sistema. A partir de npm v5.2.0 también podemos optar por no hacer esto, y en su lugar [ejecutar `gulp` de forma local](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) (el que aparece en *package.json*) usando `npx gulp` seguido de las opciones descritas a continuación. Vale para cualquier otro paquete que se pueda usar como comando de sistema sin tener que instarlarlo de forma global. 13 | 14 | - Para ejecutar las tareas que manipulan los scripts y las hojas de estilos una sóla vez, hay que ejecutar `gulp` sin argumentos (véase la tarea 'default'). 15 | 16 | - Para mantener un servidor HTTP local abierto que fuerce una recarga del navegador cada vez que haya cambios, hay que ejecutar `gulp observe`. Esto observará los cambios que se produzcan en los archivos de *src*, forzando una compilación a *dist*. A su vez, la observación de cambios en *dist* forzará una recarga del navegador. 17 | 18 | ## Notas 19 | 20 | - Se pueden instalar plugins adicionales mediante `npm install nombre-del-plugin` y cargarlos en *gulpfile.js*. Podéis encontrar muchos [aquí](https://gulpjs.com/plugins/). A partir de la versión 5 de npm (Node 8.x) se añadirán automáticamente al *package.json* (para versiones previas usar `npm install --save nombre-del-plugin`). Como los plugins de Gulp no dejan de ser módulos de npm, también podéis buscar [aquí](https://www.npmjs.com/). 21 | 22 | - El archivo *.gitignore* evita que tanto *node_modules* como *dist* sean versionados por Git. El motivo es que, como norma general, únicamente debemos subir a nuestro repo el código que hemos hecho nosotros, no el que ha hecho otra gente (ya está en sus respectivos repos y lo traemos mediante `npm install`) ni el que se genera automáticamente (ya lo generamos mediante `gulp`). 23 | 24 | - Algunas de vosotras teníais problemas al intentar ver el directorio *node_modules* desde Atom. No he encontrado ninguna info al respecto, pero podéis probar a actualizar vuestros editores a la última versión, por si fuese algún bug, o también probar algún editor alternativo ([VSCode](https://code.visualstudio.com/), [Sublime](http://www.sublimetext.com/)) para descartar otros problemas. 25 | 26 | - Hay algunos flujos de trabajo que van cambiando con el tiempo, ya que algunos se irán volviendo innecesarios y otros nuevos les reemplazarán. Es el caso, por ejemplo, de la concatenación de archivos, que es muy positiva en HTTP 1.1, pero innecesaria, e incluso contraproducente, en HTTP 2. Ocurre lo mismo con los *sprites* o concatenación de imágenes. Para saber más sobre el funcionamiento de HTTP y su influencia en el desarrollo web, recomiendo leer los capítulos dedicados a HTTP del libro [High Performance Browser Networking](http://chimera.labs.oreilly.com/books/1230000000545/index.html) de Ilya Grigorik, o echar un ojo a otra de mis presentaciones que resume el tema [aquí](keynotes/http2.pdf). 27 | 28 | ## Referencias 29 | 30 | - [Gulp for Beginners](https://css-tricks.com/gulp-for-beginners/) (CSS-Tricks). 31 | - [Diagramas de fujo de HTTP 1.1 y HTTP 2](https://twitter.com/kosamari/status/859958929484337152). 32 | 33 | ## Tiras cómicas :) 34 | 35 | - [Nodecalypse](https://webangelist.ladybenko.net/nodecalypse/), de Belén Albeza, al hilo de lo que ocurrió en el [npmgate](http://www.businessinsider.com/npm-left-pad-controversy-explained-2016-3). 36 | - [NPM Delivery](http://www.monkeyuser.com/2017/npm-delivery/), de MonkeyUser. 37 | - [Sandwich](https://www.xkcd.com/149/), de xkcd. 38 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // Gulp local (devuelve un objeto 'gulp' con todos sus métodos) 2 | var gulp = require('gulp'); 3 | 4 | // Plugins de Gulp (devuelven funciones encadenables con .pipe()) 5 | var concat = require('gulp-concat'); 6 | var uglify = require('gulp-uglify'); 7 | var rename = require('gulp-rename'); 8 | var sass = require('gulp-sass'); 9 | var cleanCss = require('gulp-clean-css'); 10 | 11 | // Otros módulos de npm (no encadenables con .pipe(), pueden devolver funciones o cualquier otra cosa) 12 | var browserSync = require('browser-sync'); 13 | 14 | // Tarea que manipula los scripts de Javascript 15 | gulp.task('script', function () { 16 | gulp.src('src/js/*.js') // Leo todos los archivos .js del directorio fuente 17 | .pipe(concat('app.js')) // Los junto todos en uno solo 18 | .pipe(uglify()) // Minifico el archivo resultante 19 | .pipe(rename('app.min.js')) // Le cambio el nombre 20 | .pipe(gulp.dest('dist/js')); // Lo escribo en el directorio destino 21 | }); 22 | 23 | // Tarea que manipula las hojas de estilos 24 | gulp.task('style', function () { 25 | gulp.src('src/css/app.scss') // Leo el archivo principal, el que contiene los includes 26 | .pipe(sass()) // Compilo el resultado a CSS, obteniendo ya un sólo archivo 27 | .pipe(cleanCss()) // Minifico el fichero resultante 28 | .pipe(rename('app.min.css')) // Le cambio el nombre 29 | .pipe(gulp.dest('dist/css')); // Lo escribo en el directorio destino 30 | }); 31 | 32 | // Tarea que ejecuta las dos anteriores de forma secuencial, se ejecuta al invocar 'gulp' sin argumentos 33 | gulp.task('default', ['script', 'style']); 34 | 35 | // Tarea que observa cambios en 'src' 36 | // En su primera ejecución lanzará también las dos tareas previas y el autoreload 37 | gulp.task('observe', ['script', 'style', 'autoreload'], function () { 38 | gulp.watch('src/css/*.scss', ['style']); // Lanza la tarea 'style' cuando observa cambios en cualquer scss 39 | gulp.watch('src/js/*.js', ['script']); // Lanza la tarea 'script' cuando observa cambios en cualquier js 40 | }); 41 | 42 | // Tarea que observa cambios en 'dist' generados desde la tarea anterior 43 | // Recarga automáticamente el navegador 44 | gulp.task('autoreload', function () { 45 | // Abre un servidor HTTP en localhost cuya raíz será nuestro directorio de tabajo 46 | // Esto es ecesario para hacer la recarga automática mediante websockets 47 | browserSync.init(['dist/css/**.css', 'dist/js/**.js', 'index.html'], { 48 | server: './' 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lista de tareas 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Mi lista de tareas

19 | 20 | 21 |
22 | 23 | 32 | 33 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /keynotes/gulp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisddm/gulp-adalab/cacdf5c8bfeb8745c38ca762d01182eaf9b700a6/keynotes/gulp.pdf -------------------------------------------------------------------------------- /keynotes/http2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisddm/gulp-adalab/cacdf5c8bfeb8745c38ca762d01182eaf9b700a6/keynotes/http2.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-adalab", 3 | "version": "1.0.0", 4 | "description": "Gulp example for Adalab", 5 | "main": "index.js", 6 | "scripts": { 7 | "gulp": "gulp" 8 | }, 9 | "author": "Adalab", 10 | "license": "MIT", 11 | "dependencies": { 12 | "browser-sync": "^2.18.13", 13 | "gulp": "^3.9.1", 14 | "gulp-clean-css": "^3.7.0", 15 | "gulp-concat": "^2.6.1", 16 | "gulp-rename": "^1.2.2", 17 | "gulp-sass": "^3.1.0", 18 | "gulp-uglify": "^3.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/css/app.scss: -------------------------------------------------------------------------------- 1 | @import 'vars'; 2 | @import 'layout'; 3 | @import 'elements'; 4 | -------------------------------------------------------------------------------- /src/css/elements.scss: -------------------------------------------------------------------------------- 1 | // Style the list 2 | 3 | ul { 4 | margin: 0; 5 | padding: 0; 6 | list-style: none; 7 | li { 8 | cursor: pointer; 9 | position: relative; 10 | padding: 12px 8px 12px 40px; 11 | background: $lighter-gray; 12 | font-size: 18px; 13 | transition: 0.2s; 14 | &:nth-child(odd) { 15 | background: darken($lighter-gray, 10%); 16 | } 17 | &:hover { 18 | background: lighten($red, 20%); 19 | } 20 | &.checked { 21 | background: $dark-gray; 22 | color: $white; 23 | text-decoration: line-through; 24 | &::before { 25 | content: ''; 26 | position: absolute; 27 | border-color: $white; 28 | border-style: solid; 29 | border-width: 0 2px 2px 0; 30 | top: 10px; 31 | left: 16px; 32 | transform: rotate(45deg); 33 | height: 15px; 34 | width: 7px; 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | /* Style the close button */ 42 | 43 | .close { 44 | cursor: pointer; 45 | padding: 12px 8px 12px 40px; 46 | background: $lighter-gray; 47 | font-size: 26px; 48 | border: 0; 49 | background-color: transparent; 50 | position: absolute; 51 | right: 0; 52 | top: 0; 53 | height: 100%; 54 | padding: 4px 16px; 55 | &:hover { 56 | background-color: $red; 57 | color: $white; 58 | } 59 | } 60 | 61 | /* Style the header */ 62 | 63 | .header { 64 | background-color: $red; 65 | padding: 30px 40px; 66 | color: $white; 67 | text-align: center; 68 | &:after { 69 | content: ''; 70 | display: table; 71 | clear: both; 72 | } 73 | } 74 | 75 | /* Style the input */ 76 | 77 | input { 78 | border: none; 79 | width: 75%; 80 | padding: 10px; 81 | float: left; 82 | font-size: 16px; 83 | } 84 | 85 | 86 | /* Style the 'Add' button */ 87 | 88 | .task-button { 89 | border: 0; 90 | padding: 10px; 91 | width: 25%; 92 | background: $light-gray; 93 | color: $darker-gray; 94 | float: left; 95 | text-align: center; 96 | font-size: 16px; 97 | cursor: pointer; 98 | transition: 0.3s; 99 | &:hover { 100 | background-color: $gray; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/css/layout.scss: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | margin: 0 auto; 3 | max-width: 800px; 4 | font-family: Arial, Helvetica, sans-serif; 5 | } 6 | 7 | .footer { 8 | font-size: 11px; 9 | } 10 | 11 | 12 | /* Include the padding and border in an element's total width and height */ 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /src/css/vars.scss: -------------------------------------------------------------------------------- 1 | $white: #fff; 2 | $lighter-gray: #eee; 3 | $light-gray: #d9d9d9; 4 | $gray: #bbb; 5 | $dark-gray: #888; 6 | $darker-gray: #555; 7 | 8 | $red: #f44336; 9 | -------------------------------------------------------------------------------- /src/js/dom.js: -------------------------------------------------------------------------------- 1 | // Load list items 2 | var $taskLi = document.querySelectorAll('li'); 3 | 4 | // Append a close button to each list item 5 | for (var i = 0; i < $taskLi.length; i++) { 6 | var $closeButton = createCloseButton(); 7 | $taskLi[i].appendChild($closeButton); 8 | } 9 | 10 | // Load list 11 | var $listUl = document.querySelector('#list'); 12 | 13 | // Add a 'checked' symbol when clicking on a list item 14 | $listUl.addEventListener('click', function (ev) { 15 | ev.target.classList.toggle('checked'); 16 | }); 17 | 18 | // Load add task button 19 | var $taskButton = document.querySelector('#task-button'); 20 | 21 | // Create and add new task 22 | $taskButton.addEventListener('click', function () { 23 | var li = document.createElement('li'); 24 | var inputValue = document.querySelector('#task-input').value; 25 | var t = document.createTextNode(inputValue); 26 | 27 | li.appendChild(t); 28 | 29 | if (!inputValue) { 30 | alert('You must write something!'); 31 | } else { 32 | document.querySelector('#list').appendChild(li); 33 | } 34 | 35 | document.querySelector('#task-input').value = ''; 36 | 37 | var $closeButton = createCloseButton(); 38 | li.appendChild($closeButton); 39 | }); 40 | -------------------------------------------------------------------------------- /src/js/functions.js: -------------------------------------------------------------------------------- 1 | // Create a close button 2 | function createCloseButton() { 3 | var $closeButton = document.createElement('button'); 4 | var txt = document.createTextNode('\u00D7'); 5 | $closeButton.className = 'close'; 6 | $closeButton.appendChild(txt); 7 | $closeButton.onclick = function () { 8 | var $div = this.parentElement; 9 | $div.style.display = 'none'; 10 | } 11 | return $closeButton; 12 | } --------------------------------------------------------------------------------