JS, Node.js, Frontend, Backend, Firebase, Express, Patrones, HTML5_APIs, Asincronía, Websockets, Testing
- Grunt - Getting started
- Grunt - Configuring tasks
- GRUNT.JS - Tutorial básico
- A Simple Guide to Getting Started With Grunt
- Grunt - Tutorialspoint
- Grunt for People Who Think Things Like Grunt are Weird and Hard
//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 Grunt 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: