Documentación
- Web Oficial
- Features
- Pricing
- Documentación
- NPM Community
- NPM en Github
- @npmjs en Twitter
- @npmstatus en Twitter
- @npm_support en Twitter
Instalar paquetes:
- global:
npm install -g <paquete>
- local:
npm install <paquete>
Buscar paquetes
npm search <paquete>
Información de los paquetes
npm view <paquete>
Lista de paquetes instalados
npm ls
Lista de paquetes instalados globalmente
npm ls -g
Instalando versiones especificas:
- la más reciente:
npm install <paquete>@latest
- versión especifica:
npm install <paquete>@1.x (1.xx.xx)
- Otra versión especifica
npm install <paquete>@2.10.x (2.10.x)
Paquetes desactualziados:
npm outdated
Actualizando paquetes:
npm update <paquete>
Desinstalando paquete:
npm uninstall <paquete>
Información sobre Bugs
npm bugs <paquete>
Abyssus abyssum invocat. El abismo llama al abismo (Un paso en falso lleva a otro).
-
Datos proyecto
-
Tareas
-
Dependencias (dependencies y devDependencies)
-
Creación:
npm init
- Guardar nuevas dependencias:
npm install <paquete> --save
- Guardar nuevas dependencias (solo para entorno desarrollo):
npm install <paquete> --save -dev
- Guardando versiones especificas:
- (1.xx.xx):
npm install --save <paquete>@1.x
- (2.10.x)
npm install --save <paquete>@2.10.x
- Latest
npm install --save <paquete>@lastest
- Quitando dependencias:
npm uninstall <paquete> --save
- Instalamos las dependencias en el proyecto:
- todo:
npm install (todo)
- Solo production:
npm install --production (solo producción)
- Solo development:
npm install --dev
- Semantic Versioning
- Estructura -> X.Y.Z-Extra
- Cambio Mayor - No retrocompatible
- Cambio Menor - Retrocompatible - Nuevas funcionaldiades o cambios
- Parche - Retrocompatible - Solución de errores
- Extras - Indicativos o versiones especiales (Beta, Alfa, x86, etc...)
- Añadiendo comandos:
// ...
"scripts": {
"test": "npm -v",
"start": "node -v",
"hola": "echo 'Hola mundo!'"
}
// ...
- Mostrando todos los comandos:
npm run
- Ejecutando comandos:
- test
npm test
- start
npm start
- hola
npm run hola
Documentación
Iniciar un proyecto
yarn init
Añadir dependencias al proyecto
yarn add [package]
yarn add [package]@[version]
yarn add [package]@[tag]
Añadir dependencias al proyecto en categorías
yarn add [package] --dev
yarn add [package] --peer
yarn add [package] --optional
Actualizar dependencias
yarn upgrade [package]
yarn upgrade [package]@[version]
yarn upgrade [package]@[tag]
Eliminar dependencias
yarn remove [package]
Instalar todas las dependencias
yarn
yarn install
**¿quien pidio este paquete?
yarn why [package]
Yarn o NPM@5?
- Yarn es mas rapido
- Yarn es más seguro
Recursos
- npm@5 — Yarn killer?
- ¿Tiene sentido Yarn ahora que tenemos npm 5?
- Does NPM 5 deprecate yarn?
- Why I’m Sticking With Yarn (Sorry NPM 5)
- The npm Blog | v5.0.0
- Why i still don't use yarn
- NPM@5 Arrived. But Is It As Fast As Yarn?
- npm vs Yarn – Which Package Manager Should You Use?
- Yarn vs npm: Everything You Need to Know
- It depends. The art of dependency management in Javascript
Web sites are made of lots of things — frameworks, libraries, assets, utilities, and rainbows. Bower manages all these things for you.
Bower Instalamos Bower globalmente
sudo npm install -g bower
Dependencias
/home/ubuntu/.nvm/versions/node/v4.1.1/bin/bower -> /home/ubuntu/.nvm/versions/node/v4.1.1/lib/node_modules/bower/bin/bower
[email protected] /home/ubuntu/.nvm/versions/node/v4.1.1/lib/node_modules/bower
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected])
├── [email protected] ([email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected])
├── [email protected] ([email protected], [email protected])
├── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected])
├── [email protected]
└── [email protected] ([email protected], [email protected], [email protected], [email protected], [email protected])
Arrancamos bower.json
bower init
{
"name": "pruebas_bower",
"homepage": "https://github.com/UlisesGascon/Curso-in-company-NexTReT",
"authors": [
"ulisesgascon"
],
"description": "",
"main": "",
"moduleType": [],
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
Buscamos paquetes
bower search <paquete>
bower search jquery
Instalando Paquetes
bower install <paquete>
bower install jquery
Instalando versiones especificas del paquete
bower install <package>#<version>
Instalando Paquetes y guardandolos en bower.json
bower install <paquete> -save
bower install bootstrap -save
ulisesgascon:~/workspace/profe/pruebas_bower (master) $ bower install bootstrap -save
bower jquery#~2.1.4 cached git://github.com/jquery/jquery.git#2.1.4
bower jquery#~2.1.4 validate 2.1.4 against git://github.com/jquery/jquery.git#~2.1.4
bower bootstrap#* not-cached git://github.com/twbs/bootstrap.git#*
bower bootstrap#* resolve git://github.com/twbs/bootstrap.git#*
bower bootstrap#* download https://github.com/twbs/bootstrap/archive/v3.3.5.tar.gz
bower bootstrap#* extract archive.tar.gz
bower bootstrap#* resolved git://github.com/twbs/bootstrap.git#3.3.5
bower jquery#~2.1.4 install jquery#2.1.4
bower bootstrap#~3.3.5 install bootstrap#3.3.5
// bower.json
{
"name": "pruebas_bower",
"homepage": "https://github.com/UlisesGascon/Curso-in-company-NexTReT",
"authors": [
"ulisesgascon"
],
"description": "",
"main": "",
"moduleType": [],
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.1.4",
"bootstrap": "~3.3.5"
}
}
Borrando paquetes
bower uninstall <paquete>
bower uninstall jquery
Borrando paquetes y guardandolos en bower.json
bower uninstall <paquete>
bower uninstall jquery
Verificando los paquetes instalados y sus dependencias
bower list
ulisesgascon:~/workspace/profe/pruebas_bower (master) $ bower list
bower check-new Checking for new versions of the project dependencies...
pruebas_bower /home/ubuntu/workspace/profe/pruebas_bower
├─┬ bootstrap#3.3.5 (latest is 4.0.0-alpha)
│ └── jquery#2.1.4 (3.0.0-alpha1+compat available)
└── jquery#2.1.4 (latest is 3.0.0-alpha1+compat)
Actualizando todo
bower update
Actualizando un paquete específico
bower update <package>
Usando Bower
bower list -paths
ulisesgascon:~/workspace/profe/pruebas_bower (master) $ bower list -paths
bootstrap: [
'public/vendor/bootstrap/less/bootstrap.less',
'public/vendor/bootstrap/dist/js/bootstrap.js'
],
jquery: 'public/vendor/jquery/dist/jquery.js'
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Bootstrap 101 Template</title>
<!-- Bootstrap -->
<link href="public/vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>Hello, world!</h1>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="public/vendor/jquery/dist/jquery.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="public/vendor/bootstrap/dist/js/bootstrap.js"></script>
</body>
</html>
Bower (Entendiendo el funcionamiento)
bower.json. ¿Qué necesitamos?
{
"name": "Mi Aplicación",
"version": "1.0.0",
"dependencies": {
"modernizr": "*",
"jquery": "~2.0.2",
"bootstrap": "*",
"requirejs": "*"
}
}
.bowerrc ¿Donde lo necesitamos?
{
"directory": "public/vendor",
"json": "bower.json"
}
Instalamos todo lo anterior
bower install
Bower (Trucos)
-
Se puede instalar componentes aislados primero y luego hacer bower init para generar el bower.json con todo incluido.
-
Ignoramos la carpeta bower_components con .gitignore. Recuerda que haciendo bower.init se instala todo de nuevo.
-
Instalación de paquetes más alla de los definidos en search:
-
Paquetes registrados:
$ bower install jquery
- Acesso directo -> GitHub:
$ bower install desandro/masonry
- .git:
$ bower install git://github.com/user/package.git
- URLs:
$ bower install http://example.com/script.js
Limitaciones
- Gestión de conflictos manual
- Fiarnos del patrón SEMVER por parte de otr@s developers
- Sistema de paquetes propio
- Redundancia respecto a NPM
- Maravillo mundo del Dependency hell
BOWER is dead. Only use it for legacy reasons.
Literatura al respecto
- Bower/bower #2298: Consider deprecating Bower
- Bower is dead, long live npm. And Yarn. And webpack in snyk.io/blog by Assaf Hefetz
- Reddit: Is bower dead? Should we start using other dependency managers?
- Quora: Is Bower dying?
Paso 1: Tus herramientas son las de siempre
Paso 2: Pasar del bower.json
al package.json
Paso 3: Deja de usar wiredep
y empieza a usar solamente require()
Nota: Esto es al estilo Browserify exclusivamente
- wiredep: Wire Bower dependencies to your source code
- browserify: browser-side require() the node.js way
- Debes instalar browserify de forma global con
npm install -g browserify
- Ejemplo de carga de dependencias
var $ = require('jquery'), _ = require('lodash'), angular = require('angular'); //..
- Puedes comprimir y unificar todo el JS en un solo comando
browserify app.js -o bundle.js
y en un único fichero<script src="bundle.js"></script>
- Si ademas instalas watchify con
watchify app.js -o bundle.js
refescas los cambios en tiempo real.
Extra 1: Cosas Interesantes
Extra 2: Un mundo de información te espera
- Why We Should Stop Using Bower – And How to Do It
- Browserify VS Webpack - JS Drama
- npm and front-end packaging
- The jQuery Module Fallacy
//export default grunt => {
module.exports = grunt => {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
Caractísticas
- Filosofía de configuración sobre codigo
- Basado en archivos temporales
- Ecosistema fuerte
- Poco flexible para cosas fuera de lo común
- Ficheros de configuración sobredimensionados
- Facilmente se desactualiza
Instalación
- Instalamos Gulp global
npm install -g grunt-cli
- Incluimos la dependencia en package.json
npm install grunt --save-dev
Tareas por defecto
- Creamos gruntfile.js y encapsulamos
- Definición
module.exports = grunt => {
//... resto del código de grunt...
};
- Instanciamos la configuración
module.exports = grunt => {
grunt.initConfig({
//...resto de configuración...
})
//... resto del código de grunt...
};
- Cargamos las dependencias (plugins)...
module.exports = grunt => {
grunt.initConfig({
//...resto de configuración...
})
//... resto del código de grunt...
};
grunt.loadNpmTasks('grunt-contrib-NOMBRE-PLUGIN');
grunt.loadNpmTasks('grunt-contrib-NOMBRE-PLUGIN');
//...
- Creamos la tarea por defecto
module.exports = grunt => {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
}
})
};
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']);
- Creamos tarea watch...
module.exports = grunt => {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
})
};
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']);
Lanzamiento
- Lanzar la tarea por defecto
grunt
- Lanzar el watch
grunt watch
- lanzar una tarea personalizada
grunt TAREA
- The 26 grunt plugins we use at qrator.com | Andy's Blog
- Living list of most useful plugins for Grunt
- 6 essential Grunt plugins you should be using | Creative Bloq
- 7 Essential Plug-ins for those New to GruntJS - Web Design Ledger
- Essential Grunt Plugins - Donna Peplinskie
- Psychedelic Grunt Plugins - Web Design Weekly
- 5 Grunt Plugins for a Better Workflow | Chase Adams
- Building a Better Grunt Plugin | Sprout Social
- Supercharging your Gruntfile
- Take Grunt to the Next Level — Jonathan Suh
Grunt: gruntfile.js
// Generated on 2018-03-31 using
// generator-webapp 1.1.2
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// If you want to recursively match all subfolders, use:
// 'test/spec/**/*.js'
//export default grunt => {
module.exports = grunt => {
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Automatically load required grunt tasks
require('jit-grunt')(grunt, {
useminPrepare: 'grunt-usemin'
});
// Configurable paths
const config = {
app: 'app',
dist: 'dist'
};
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
config,
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
babel: {
files: ['<%= config.app %>/scripts/{,*/}*.js'],
tasks: ['babel:dist']
},
babelTest: {
files: ['test/spec/{,*/}*.js'],
tasks: ['babel:test', 'test:watch']
},
gruntfile: {
files: ['Gruntfile.js']
},
sass: {
files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
tasks: ['sass', 'postcss']
},
styles: {
files: ['<%= config.app %>/styles/{,*/}*.css'],
tasks: ['newer:copy:styles', 'postcss']
}
},
browserSync: {
options: {
notify: false,
background: true,
watchOptions: {
ignored: ''
}
},
livereload: {
options: {
files: [
'<%= config.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'<%= config.app %>/images/{,*/}*',
'.tmp/scripts/{,*/}*.js'
],
port: 9000,
server: {
baseDir: ['.tmp', config.app],
routes: {
'/bower_components': './bower_components'
}
}
}
},
test: {
options: {
port: 9001,
open: false,
logLevel: 'silent',
host: 'localhost',
server: {
baseDir: ['.tmp', './test', config.app],
routes: {
'/bower_components': './bower_components'
}
}
}
},
dist: {
options: {
background: false,
server: '<%= config.dist %>'
}
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= config.dist %>/*',
'!<%= config.dist %>/.git*'
]
}]
},
server: '.tmp'
},
// Make sure code styles are up to par and there are no obvious mistakes
eslint: {
target: [
'Gruntfile.js',
'<%= config.app %>/scripts/{,*/}*.js',
'!<%= config.app %>/scripts/vendor/*',
'test/spec/{,*/}*.js'
]
},
// Mocha testing framework configuration options
mocha: {
all: {
options: {
run: true,
urls: ['http://<%= browserSync.test.options.host %>:<%= browserSync.test.options.port %>/index.html']
}
}
},
// Compiles ES6 with Babel
babel: {
options: {
sourceMap: true,
presets: ['es2015']
},
dist: {
files: [{
expand: true,
cwd: '<%= config.app %>/scripts',
src: '{,*/}*.js',
dest: '.tmp/scripts',
ext: '.js'
}]
},
test: {
files: [{
expand: true,
cwd: 'test/spec',
src: '{,*/}*.js',
dest: '.tmp/spec',
ext: '.js'
}]
}
},
// Compiles Sass to CSS and generates necessary files if requested
sass: {
options: {
sourceMap: true,
sourceMapEmbed: true,
sourceMapContents: true,
includePaths: ['.']
},
dist: {
files: [{
expand: true,
cwd: '<%= config.app %>/styles',
src: ['*.{scss,sass}'],
dest: '.tmp/styles',
ext: '.css'
}]
}
},
postcss: {
options: {
map: true,
processors: [
// Add vendor prefixed styles
require('autoprefixer')({
browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']
})
]
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
// Automatically inject Bower components into the HTML file
wiredep: {
app: {
src: ['<%= config.app %>/index.html'],
exclude: ['bootstrap.js'],
ignorePath: /^(\.\.\/)*\.\./
},
sass: {
src: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
ignorePath: /^(\.\.\/)+/
}
},
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= config.dist %>/scripts/{,*/}*.js',
'<%= config.dist %>/styles/{,*/}*.css',
'<%= config.dist %>/images/{,*/}*.*',
'<%= config.dist %>/fonts/{,*/}*.*',
'<%= config.dist %>/*.{ico,png}'
]
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
options: {
dest: '<%= config.dist %>'
},
html: '<%= config.app %>/index.html'
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
options: {
assetsDirs: [
'<%= config.dist %>',
'<%= config.dist %>/images',
'<%= config.dist %>/styles'
]
},
html: ['<%= config.dist %>/{,*/}*.html'],
css: ['<%= config.dist %>/styles/{,*/}*.css']
},
// The following *-min tasks produce minified files in the dist folder
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= config.app %>/images',
src: '{,*/}*.{gif,jpeg,jpg,png}',
dest: '<%= config.dist %>/images'
},{
expand: true,
cwd: '<%= config.app %>',
src: '*.{ico,png}',
dest: '<%= config.dist %>'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= config.app %>/images',
src: '{,*/}*.svg',
dest: '<%= config.dist %>/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
conservativeCollapse: true,
removeAttributeQuotes: true,
removeCommentsFromCDATA: true,
removeEmptyAttributes: true,
removeOptionalTags: true,
// true would impact styles with attribute selectors
removeRedundantAttributes: false,
useShortDoctype: true
},
files: [{
expand: true,
cwd: '<%= config.dist %>',
src: '{,*/}*.html',
dest: '<%= config.dist %>'
}]
}
},
// By default, your `index.html`'s <!-- Usemin block --> will take care
// of minification. These next options are pre-configured if you do not
// wish to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= config.dist %>/styles/main.css': [
// '.tmp/styles/{,*/}*.css',
// '<%= config.app %>/styles/{,*/}*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= config.dist %>/scripts/scripts.js': [
// '<%= config.dist %>/scripts/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= config.app %>',
dest: '<%= config.dist %>',
src: [
'*.txt',
'images/{,*/}*.webp',
'{,*/}*.html',
'fonts/{,*/}*.*'
]
}, {
expand: true,
dot: true,
cwd: '.',
src: 'bower_components/bootstrap-sass/assets/fonts/bootstrap/*',
dest: '<%= config.dist %>'
}]
}
},
// Generates a custom Modernizr build that includes only the tests you
// reference in your app
modernizr: {
dist: {
devFile: 'bower_components/modernizr/modernizr.js',
outputFile: '<%= config.dist %>/scripts/vendor/modernizr.js',
files: {
src: [
'<%= config.dist %>/scripts/{,*/}*.js',
'<%= config.dist %>/styles/{,*/}*.css',
'!<%= config.dist %>/scripts/vendor/*'
]
},
uglify: true
}
},
// Run some tasks in parallel to speed up build process
concurrent: {
server: [
'babel:dist',
'sass'
],
test: [
'babel'
],
dist: [
'babel',
'sass',
'imagemin',
'svgmin'
]
}
});
grunt.registerTask('serve', 'start the server and preview your app', target => {
if (target === 'dist') {
return grunt.task.run(['build', 'browserSync:dist']);
}
grunt.task.run([
'clean:server',
'wiredep',
'concurrent:server',
'postcss',
'browserSync:livereload',
'watch'
]);
});
grunt.registerTask('server', target => {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run([target ? (`serve:${target}`) : 'serve']);
});
grunt.registerTask('test', target => {
if (target !== 'watch') {
grunt.task.run([
'clean:server',
'concurrent:test',
'postcss'
]);
}
grunt.task.run([
'browserSync:test',
'mocha'
]);
});
grunt.registerTask('build', [
'clean:dist',
'wiredep',
'useminPrepare',
'concurrent:dist',
'postcss',
'concat',
'cssmin',
'uglify',
'copy:dist',
'modernizr',
'filerev',
'usemin',
'htmlmin'
]);
grunt.registerTask('default', [
'newer:eslint',
'test',
'build'
]);
};
Caractísticas
- Filosofía de código sobre configuración
- Basado en stream
- No es necesario usar archivos temporales
- Claridad en creación de tareas y seguimiento de procesos
- Gran cantidad de Plugins
- Cuenta con una comunidad sólida y madura
- El sistema de
streams
yPromises
no es sencillo para developers juniors
Instalación
- Instalamos Gulp global
npm install --global gulp
- Incluimos la dependencia en package.json
npm install --save-dev gulp
Tareas por defecto
- Creamos gulpfile.js y agregamos dependencias y la primera tarea por defecto
- Definición
//import gulp from 'gulp';
const gulp = require('gulp');
gulp.task('default', () => {
console.log("Estas en la tarea por defecto!")
});
- Lanzamiento
gulp
Más tareas
- Creamos una tarea nueva para gestionar la concatenación y minificación de los archivos js.
- Definición
/*
import gulp from 'gulp';
import concat from 'gulp-concat';
import uglify from 'gulp-uglify';
*/
const gulp = require('gulp'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify');
gulp.task('concat-ugly', () => {
console.log("Estas en la tarea de concatenación!")
gulp.src('js/sources/*.js')
.pipe(concat('app.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js'))
});
- Instalamos las nuevas dependencias
npm install -save gulp-concat && npm install -save gulp-uglify
- Lanzamiento
gulp concat-ugly
Agrupando tareas
- Definición
gulp.task('distro-lista', ['imagenes', 'css', 'js']);
- Lanzamiento
gulp distro-lista
Concatenando tareas
- Definición
gulp.task('css-paso-2', ['css-paso-1'], () => {
console.log("css-paso-2 empieza solo cuando... css-paso-1 haya termiando!")
});
- Lanzamiento
gulp css-paso-2
Entendiendo Gulp
gulp.src()
y gulp.dest()
- Un solo archivo
gulp.src('client/templates/index.jade')
// .pipe(...)
- Múltiples archivos
gulp.src(['client/*.js', '!client/b*.js', 'client/bad.js'])
// .pipe(...)
- Múltiples archivos y carpetas
gulp.src('client/templates/**/*.jade')
// .pipe(...)
- Exclusión
!js/secreto-config.js
- Especificando la extensión
publico/*.+(js|css)
gulp.watch()
- Monitoriza de manera activa uno o varios archivos y dispara tareas específicas cuando se hayan modificado
gulp.watch('js/source/*.js', ['js']);
- gulp-debug: BÁSICO para debugear las tareas de GULP y los ficheros en scope de la tarea
- gulp-concat: Concatenación de archivos
- gulp-uglify: Comprime javascript usando UglifyJS2
- gulp-stylus: Compilar de .styl a .css
- gulp-coffee: Compilar de .coffee a .js
- gulp-jade: Compilador de .jade a .html
- gulp-if: Control adiccional para el flujo de subtareas
- gulp-imagemin: Minificación de imágenes con formato .png, .jpeg, .gif y .svg, más opciones
- gulp-jshint: JSHint
- gulp.spritesmith: Crea Sprites y el css adiccional en diversos formatos (.css, .json, Sass, Less)
- gulp-zip: Compresor ZIP
- gulp-csslint: CSS Linter
- gulp-eslint: ESLint
- gulp-gh-pages: Gestiona la publicación en GitHub Pages
- gulp-git: Gestiona Git desde Gulp
- gulp-htmlmin: Minificador de HTML
- gulp-iconfont: Creando fuentes de Iconos desde archivos vectoriales
- gulp-jsonlint: Linter para json
- gulp-markdown: Markdown a HTML
- gulp-sourcemaps: Crea SourceMaps
- gulp-uncss: Elimina CSS que no se use
- gulp-jsdoc-to-markdown: Conversor de jsdocs a markdown
- gulp-unzip: Descompresor ZIP
- gulp-webstandards: Verifica prefijos CSS, Versión de librerías js, dcoType, compatibildiad entre navegadores, etc...
- gulp-filesize: Muestra el tamaño de los archivos.
- gulp-grunt: Tareas de Grutn funcionan en Gulp
- gulp-shell: Manejando comandos de terminal
- Easy Accessibility Testing with aXe
- puppeteer: Headless Chrome Node API
- pageres: Genera pantallazos de la web en diversos tamaños
- PSI: PageSpeed Insights desde la terminal
- Lighthouse: Auditing, performance metrics, and best practices for Progressive Web Apps
Gulp: gulpfile.js
// generated on 2018-03-31 using generator-webapp 3.0.1
const gulp = require('gulp');
const gulpLoadPlugins = require('gulp-load-plugins');
const browserSync = require('browser-sync').create();
const del = require('del');
const wiredep = require('wiredep').stream;
const runSequence = require('run-sequence');
const $ = gulpLoadPlugins();
const reload = browserSync.reload;
let dev = true;
gulp.task('styles', () => {
return gulp.src('app/styles/*.scss')
.pipe($.plumber())
.pipe($.if(dev, $.sourcemaps.init()))
.pipe($.sass.sync({
outputStyle: 'expanded',
precision: 10,
includePaths: ['.']
}).on('error', $.sass.logError))
.pipe($.autoprefixer({browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']}))
.pipe($.if(dev, $.sourcemaps.write()))
.pipe(gulp.dest('.tmp/styles'))
.pipe(reload({stream: true}));
});
gulp.task('scripts', () => {
return gulp.src('app/scripts/**/*.js')
.pipe($.plumber())
.pipe($.if(dev, $.sourcemaps.init()))
.pipe($.babel())
.pipe($.if(dev, $.sourcemaps.write('.')))
.pipe(gulp.dest('.tmp/scripts'))
.pipe(reload({stream: true}));
});
function lint(files) {
return gulp.src(files)
.pipe($.eslint({ fix: true }))
.pipe(reload({stream: true, once: true}))
.pipe($.eslint.format())
.pipe($.if(!browserSync.active, $.eslint.failAfterError()));
}
gulp.task('lint', () => {
return lint('app/scripts/**/*.js')
.pipe(gulp.dest('app/scripts'));
});
gulp.task('lint:test', () => {
return lint('test/spec/**/*.js')
.pipe(gulp.dest('test/spec'));
});
gulp.task('html', ['styles', 'scripts'], () => {
return gulp.src('app/*.html')
.pipe($.useref({searchPath: ['.tmp', 'app', '.']}))
.pipe($.if(/\.js$/, $.uglify({compress: {drop_console: true}})))
.pipe($.if(/\.css$/, $.cssnano({safe: true, autoprefixer: false})))
.pipe($.if(/\.html$/, $.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: {compress: {drop_console: true}},
processConditionalComments: true,
removeComments: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
})))
.pipe(gulp.dest('dist'));
});
gulp.task('images', () => {
return gulp.src('app/images/**/*')
.pipe($.cache($.imagemin()))
.pipe(gulp.dest('dist/images'));
});
gulp.task('fonts', () => {
return gulp.src(require('main-bower-files')('**/*.{eot,svg,ttf,woff,woff2}', function (err) {})
.concat('app/fonts/**/*'))
.pipe($.if(dev, gulp.dest('.tmp/fonts'), gulp.dest('dist/fonts')));
});
gulp.task('extras', () => {
return gulp.src([
'app/*',
'!app/*.html'
], {
dot: true
}).pipe(gulp.dest('dist'));
});
gulp.task('clean', del.bind(null, ['.tmp', 'dist']));
gulp.task('serve', () => {
runSequence(['clean', 'wiredep'], ['styles', 'scripts', 'fonts'], () => {
browserSync.init({
notify: false,
port: 9000,
server: {
baseDir: ['.tmp', 'app'],
routes: {
'/bower_components': 'bower_components'
}
}
});
gulp.watch([
'app/*.html',
'app/images/**/*',
'.tmp/fonts/**/*'
]).on('change', reload);
gulp.watch('app/styles/**/*.scss', ['styles']);
gulp.watch('app/scripts/**/*.js', ['scripts']);
gulp.watch('app/fonts/**/*', ['fonts']);
gulp.watch('bower.json', ['wiredep', 'fonts']);
});
});
gulp.task('serve:dist', ['default'], () => {
browserSync.init({
notify: false,
port: 9000,
server: {
baseDir: ['dist']
}
});
});
gulp.task('serve:test', ['scripts'], () => {
browserSync.init({
notify: false,
port: 9000,
ui: false,
server: {
baseDir: 'test',
routes: {
'/scripts': '.tmp/scripts',
'/bower_components': 'bower_components'
}
}
});
gulp.watch('app/scripts/**/*.js', ['scripts']);
gulp.watch(['test/spec/**/*.js', 'test/index.html']).on('change', reload);
gulp.watch('test/spec/**/*.js', ['lint:test']);
});
// inject bower components
gulp.task('wiredep', () => {
gulp.src('app/styles/*.scss')
.pipe($.filter(file => file.stat && file.stat.size))
.pipe(wiredep({
ignorePath: /^(\.\.\/)+/
}))
.pipe(gulp.dest('app/styles'));
gulp.src('app/*.html')
.pipe(wiredep({
exclude: ['bootstrap-sass'],
ignorePath: /^(\.\.\/)*\.\./
}))
.pipe(gulp.dest('app'));
});
gulp.task('build', ['lint', 'html', 'images', 'fonts', 'extras'], () => {
return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true}));
});
gulp.task('default', () => {
return new Promise(resolve => {
dev = false;
runSequence(['clean', 'wiredep'], 'build', resolve);
});
});
- Nuevo sistema de dependencias
gulp.task()
solo admite 2 argumentos gulp.series()
y 'gulp.parallel()' son ahora nativas. ¡Adios runSequence!.- El orden de las tareas es clave y ya no podemos organizarlas como queramos, deben ir por orden de carga
- Es necesario terminar cada tarea con un callback o resolver la promesa. En Gulp3 no era necesario.
- Por el momento tiene poca adopción
Si tienes un
gulpile.js
funcionando con Gulp 3.x, no merece la pena migrarse.
Antes - Gulp 3.9.x
gulp.task('a', function () {
// Do something.
});
gulp.task('b', ['a'], function () {
// Do some stuff.
});
gulp.task('c', ['b'], function () {
// Do some more stuff.
});
Ahora - Gulp 4.x
- Las tareas solo tienen 2 argumentos.
- La concatenación de tareas puede ser serializada con
gulp.series()
o paralelizada con 'gulp.parallel()'
gulp.task('my-tasks', gulp.series('a', 'b', 'c', function() {
// Do something after a, b, and c are finished.
}));
gulp.task('build', gulp.parallel('styles', 'scripts', 'images', function () {
// Build the website.
}));
- The Complete-Ish Guide to Upgrading to Gulp 4
- Web Tooling and Automatisation using gulp 4
- How to install Gulp 4 before it's officially released
- Migrating to gulp 4 by example
- How to Upgrade to Gulp 4
- Getting started with Gulp 4 for Angular
- A quick guide for switching to gulp 4
/*
import gulp from 'gulp';
import less from 'gulp-less';
import babel from 'gulp-babel';
import concat from 'gulp-concat';
import uglify from 'gulp-uglify';
import rename from 'gulp-rename';
import cleanCSS from 'gulp-clean-css';
import del from 'del';
*/
const gulp = require('gulp'),
less = require('gulp-less'),
babel = require('gulp-babel'),
concat = require('gulp-concat'),
uglify = require('gulp-uglify'),
rename = require('gulp-rename'),
cleanCSS = require('gulp-clean-css'),
del = require('del');
const paths = {
styles: {
src: 'src/styles/**/*.less',
dest: 'assets/styles/'
},
scripts: {
src: 'src/scripts/**/*.js',
dest: 'assets/scripts/'
}
};
/* Not all tasks need to use streams, a gulpfile is just another node program
* and you can use all packages available on npm, but it must return either a
* Promise, a Stream or take a callback and call it
*/
function clean() {
// You can use multiple globbing patterns as you would with `gulp.src`,
// for example if you are using del 2.0 or above, return its promise
return del([ 'assets' ]);
}
/*
* Define our tasks using plain functions
*/
function styles() {
return gulp.src(paths.styles.src)
.pipe(less())
.pipe(cleanCSS())
// pass in options to the stream
.pipe(rename({
basename: 'main',
suffix: '.min'
}))
.pipe(gulp.dest(paths.styles.dest));
}
function scripts() {
return gulp.src(paths.scripts.src, { sourcemaps: true })
.pipe(babel())
.pipe(uglify())
.pipe(concat('main.min.js'))
.pipe(gulp.dest(paths.scripts.dest));
}
function watch() {
gulp.watch(paths.scripts.src, scripts);
gulp.watch(paths.styles.src, styles);
}
// You can use CommonJS `exports` module notation to declare tasks
/*
export {clean};
export {styles};
export {scripts};
export {watch};
*/
exports.clean = clean;
exports.styles = styles;
exports.scripts = scripts;
exports.watch = watch;
// Specify if tasks run in series or parallel using `gulp.series` and `gulp.parallel`
const build = gulp.series(clean, gulp.parallel(styles, scripts));
// You can still use `gulp.task` to expose tasks
gulp.task('build', build);
// Define default task that can be called by just running `gulp` from cli
gulp.task('default', build);
Grunt:
Gulp:
Recursos
- Why I Left Gulp and Grunt for npm Scripts by Cory House
- Task automation with npm run by James Holliday
- Advanced front-end automation with npm scripts by Kate Hudson
- How to use npm as a build tool by Kieth Cirkel
- Introduction to npm as a Build Tool by Marcus Hammarberg
- Gulp is awesome, but do we really need it? by Gonto
- No conoces a Ashley Williams ni has visto "You Don't Know npm". Slides
- Existen convenciones en el
package.json
que solemos ignorar comopreinstall
,install
,postinstall
,uninstall
,test
,start
, etc... - Puedes pasarte variables de entorno en los comandos
NODE_ENV=production server.js
- Puedes ejecutar otros comandos del
package.json
, con solo mencionarlosnpm run otro-comando && echo otras cosas después...
- Si quieres mantener soporte con todas las plataformas, debes evitar el uso de comandos de shell y abstraerlo con dependencias. Por ejemplo
rm -rf /
vs rimraf - Puedes agrupar comandos en ficheros y siplemente ejecutarlos como una tarea más,
node fichero-comandos.js
- No agrupes comandos complejos o muy largos, divide y venceras
- Recuerda que existen multiples operadores en Linux y OSX:
- Puedes paralelismo dentro del mismo comando usando el operador
&
y no unicamente&&
, ejemplo:npm run watch-js & npm run watch-css
- Puedes unir dos tareas usando
&&
. La seguna tarea solo se ejecutará cuando la primera haya concluido con éxito, ejemplo:npm install && npm start
- Puedes usar
;
para ejecutar el siguiente comando aun cuando el primero no funcionó correctamente, ejemplo:npm run algo-raro; npm run watch-css
- Puedes usar
||
para ejecutar el siguiente comando solo cuando el primero no funcionó correctamente, ejemplo:npm install-unix || npm install windows
- Puedes combinar comandos como
[ -f ~/fichero.txt ] && echo "el fichero existe." || touch ~/fichero.txt
, es decir, si el fichero existe me avisa, pero si no... lo crea!
- Puedes paralelismo dentro del mismo comando usando el operador
- Si un comando es muy largo... solo necesitas crearte un
fichero.sh
encabezado por#!/bin/bash
y agrupando comandos con parentesis. No te olvides de los permisos de ejecuación conchmod +x FICHERO.sh
#!/bin/bash
(cd site/main; echo banana)
(cd site/xyz; echo lorem ipsum...)