Gulp Cheatsheet¶
Gulp - The Streaming Build System
Gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something. It uses Node.js streams, making builds faster by avoiding the need to write temporary files to disk.
Table of Contents¶
- Installation
- Getting Started
- Core Concepts
- Basic Tasks
- File Operations
- CSS Processing
- JavaScript Processing
- Image Optimization
- HTML Processing
- Development Server
- Watch Tasks
- Build Pipeline
- Plugin Ecosystem
- Advanced Patterns
- Performance Optimization
- Error Handling
- Testing
- Deployment
- Best Practices
Installation¶
Global Installation¶
Local Installation¶
# Initialize npm project
npm init -y
# Install Gulp locally
npm install --save-dev gulp
# Install common plugins
npm install --save-dev gulp-sass gulp-uglify gulp-concat gulp-clean-css
Project Setup¶
# Create project structure
mkdir my-gulp-project
cd my-gulp-project
# Initialize package.json
npm init -y
# Install Gulp and plugins
npm install --save-dev gulp gulp-sass gulp-autoprefixer gulp-clean-css gulp-uglify gulp-concat gulp-imagemin gulp-htmlmin
# Create gulpfile
touch gulpfile.js
# Create source directories
mkdir -p src/{scss,js,images,html}
mkdir dist
Package.json Scripts¶
{
"name": "my-gulp-project",
"version": "1.0.0",
"scripts": {
"build": "gulp build",
"dev": "gulp dev",
"watch": "gulp watch",
"clean": "gulp clean",
"serve": "gulp serve"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-sass": "^5.1.0",
"gulp-autoprefixer": "^8.0.0",
"gulp-clean-css": "^4.3.0",
"gulp-uglify": "^3.0.2",
"gulp-concat": "^2.6.1",
"gulp-imagemin": "^8.0.0",
"gulp-htmlmin": "^5.0.1"
}
}
Getting Started¶
Basic Gulpfile¶
// gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const cleanCSS = require('gulp-clean-css');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
// Define paths
const paths = {
styles: {
src: 'src/scss/**/*.scss',
dest: 'dist/css/'
},
scripts: {
src: 'src/js/**/*.js',
dest: 'dist/js/'
}
};
// Compile Sass
function styles() {
return gulp.src(paths.styles.src)
.pipe(sass().on('error', sass.logError))
.pipe(cleanCSS())
.pipe(gulp.dest(paths.styles.dest));
}
// Process JavaScript
function scripts() {
return gulp.src(paths.scripts.src)
.pipe(concat('main.js'))
.pipe(uglify())
.pipe(gulp.dest(paths.scripts.dest));
}
// Watch files
function watch() {
gulp.watch(paths.styles.src, styles);
gulp.watch(paths.scripts.src, scripts);
}
// Export tasks
exports.styles = styles;
exports.scripts = scripts;
exports.watch = watch;
exports.build = gulp.parallel(styles, scripts);
exports.default = gulp.series(exports.build, watch);
Running Tasks¶
# Run individual tasks
gulp styles
gulp scripts
# Run build task
gulp build
# Run default task (build + watch)
gulp
# Run with specific gulpfile
gulp --gulpfile custom.gulpfile.js
# List available tasks
gulp --tasks
Project Structure¶
my-gulp-project/
├── src/
│ ├── scss/
│ │ ├── main.scss
│ │ ├── _variables.scss
│ │ └── components/
│ ├── js/
│ │ ├── main.js
│ │ └── modules/
│ ├── images/
│ └── html/
├── dist/
│ ├── css/
│ ├── js/
│ ├── images/
│ └── index.html
├── gulpfile.js
└── package.json
Core Concepts¶
Streams and Pipes¶
// Basic stream example
gulp.src('src/**/*.js') // Read files
.pipe(uglify()) // Transform
.pipe(gulp.dest('dist/')); // Write files
// Multiple transformations
gulp.src('src/scss/**/*.scss')
.pipe(sass()) // Compile Sass
.pipe(autoprefixer()) // Add vendor prefixes
.pipe(cleanCSS()) // Minify CSS
.pipe(gulp.dest('dist/css/'));
Glob Patterns¶
// Glob pattern examples
gulp.src('src/*.js') // All .js files in src/
gulp.src('src/**/*.js') // All .js files in src/ and subdirectories
gulp.src('src/js/*.{js,ts}') // All .js and .ts files in src/js/
gulp.src(['src/**/*.js', '!src/vendor/**']) // Exclude vendor directory
// Multiple sources
gulp.src([
'src/js/vendor/*.js',
'src/js/main.js',
'src/js/modules/*.js'
])
Task Functions¶
// Named function
function buildCSS() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('dist/css/'));
}
// Arrow function
const buildJS = () => {
return gulp.src('src/js/**/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist/js/'));
};
// Async function
async function copyFiles() {
return gulp.src('src/assets/**/*')
.pipe(gulp.dest('dist/assets/'));
}
// Export tasks
exports.css = buildCSS;
exports.js = buildJS;
exports.copy = copyFiles;
Series and Parallel¶
const { series, parallel } = require('gulp');
// Run tasks in series (one after another)
const build = series(clean, parallel(styles, scripts), copyAssets);
// Run tasks in parallel (simultaneously)
const dev = parallel(styles, scripts, serve, watch);
// Mixed series and parallel
const deploy = series(
clean,
parallel(styles, scripts, images),
copyFiles,
uploadToServer
);
exports.build = build;
exports.dev = dev;
exports.deploy = deploy;
Basic Tasks¶
File Copying¶
// Simple file copy
function copyHTML() {
return gulp.src('src/**/*.html')
.pipe(gulp.dest('dist/'));
}
// Copy with base option
function copyAssets() {
return gulp.src('src/assets/**/*', { base: 'src' })
.pipe(gulp.dest('dist/'));
}
// Copy specific files
function copyFonts() {
return gulp.src([
'src/fonts/**/*.{woff,woff2,ttf,eot}',
'node_modules/font-awesome/fonts/**/*'
])
.pipe(gulp.dest('dist/fonts/'));
}
File Cleaning¶
const del = require('del');
// Clean build directory
function clean() {
return del(['dist/**', '!dist']);
}
// Clean specific files
function cleanCSS() {
return del(['dist/css/**/*.css']);
}
// Clean with patterns
function cleanOld() {
return del([
'dist/**',
'!dist/vendor',
'!dist/vendor/**'
]);
}
exports.clean = clean;
File Renaming¶
const rename = require('gulp-rename');
// Add suffix
function minifyCSS() {
return gulp.src('src/css/**/*.css')
.pipe(cleanCSS())
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('dist/css/'));
}
// Change extension
function compileTypeScript() {
return gulp.src('src/**/*.ts')
.pipe(typescript())
.pipe(rename({ extname: '.js' }))
.pipe(gulp.dest('dist/js/'));
}
// Custom rename function
function customRename() {
return gulp.src('src/**/*.scss')
.pipe(sass())
.pipe(rename((path) => {
path.dirname += '/compiled';
path.basename += '-processed';
}))
.pipe(gulp.dest('dist/css/'));
}
File Operations¶
File Concatenation¶
const concat = require('gulp-concat');
// Concatenate JavaScript files
function concatJS() {
return gulp.src([
'src/js/vendor/*.js',
'src/js/modules/*.js',
'src/js/main.js'
])
.pipe(concat('bundle.js'))
.pipe(gulp.dest('dist/js/'));
}
// Concatenate CSS files
function concatCSS() {
return gulp.src('src/css/**/*.css')
.pipe(concat('styles.css'))
.pipe(gulp.dest('dist/css/'));
}
// Conditional concatenation
function buildVendorJS() {
return gulp.src([
'node_modules/jquery/dist/jquery.min.js',
'node_modules/bootstrap/dist/js/bootstrap.min.js',
'src/js/vendor/*.js'
])
.pipe(concat('vendor.js'))
.pipe(gulp.dest('dist/js/'));
}
File Filtering¶
const gulpif = require('gulp-if');
const filter = require('gulp-filter');
// Conditional processing
function processFiles() {
const isProduction = process.env.NODE_ENV === 'production';
return gulp.src('src/js/**/*.js')
.pipe(gulpif(isProduction, uglify()))
.pipe(gulp.dest('dist/js/'));
}
// Filter specific files
function processAssets() {
const jsFilter = filter('**/*.js', { restore: true });
const cssFilter = filter('**/*.css', { restore: true });
return gulp.src('src/**/*')
.pipe(jsFilter)
.pipe(uglify())
.pipe(jsFilter.restore)
.pipe(cssFilter)
.pipe(cleanCSS())
.pipe(cssFilter.restore)
.pipe(gulp.dest('dist/'));
}
File Transformation¶
const replace = require('gulp-replace');
const template = require('gulp-template');
// String replacement
function updateVersion() {
return gulp.src('src/**/*.js')
.pipe(replace('{{VERSION}}', process.env.npm_package_version))
.pipe(gulp.dest('dist/js/'));
}
// Template processing
function processTemplates() {
return gulp.src('src/templates/**/*.html')
.pipe(template({
title: 'My Website',
version: '1.0.0',
timestamp: new Date().toISOString()
}))
.pipe(gulp.dest('dist/'));
}
// Multiple replacements
function processConfig() {
return gulp.src('src/config.js')
.pipe(replace('{{API_URL}}', process.env.API_URL || 'http://localhost:3000'))
.pipe(replace('{{DEBUG}}', process.env.NODE_ENV === 'development'))
.pipe(gulp.dest('dist/js/'));
}
CSS Processing¶
Sass Compilation¶
const sass = require('gulp-sass')(require('sass'));
const autoprefixer = require('gulp-autoprefixer');
const cleanCSS = require('gulp-clean-css');
const sourcemaps = require('gulp-sourcemaps');
// Basic Sass compilation
function compileSass() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('dist/css/'));
}
// Advanced Sass processing
function processCSS() {
return gulp.src('src/scss/main.scss')
.pipe(sourcemaps.init())
.pipe(sass({
outputStyle: 'expanded',
includePaths: ['node_modules']
}).on('error', sass.logError))
.pipe(autoprefixer({
cascade: false
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/css/'))
.pipe(cleanCSS())
.pipe(rename({ suffix: '.min' }))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/css/'));
}
// Production CSS build
function buildProductionCSS() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass({
outputStyle: 'compressed',
includePaths: ['node_modules/bootstrap/scss']
}))
.pipe(autoprefixer({
browsers: ['last 2 versions', '> 1%'],
cascade: false
}))
.pipe(cleanCSS({
level: 2
}))
.pipe(gulp.dest('dist/css/'));
}
PostCSS Processing¶
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const tailwindcss = require('tailwindcss');
// PostCSS with plugins
function processPostCSS() {
const plugins = [
tailwindcss(),
autoprefixer(),
cssnano()
];
return gulp.src('src/css/**/*.css')
.pipe(postcss(plugins))
.pipe(gulp.dest('dist/css/'));
}
// Tailwind CSS processing
function buildTailwind() {
return gulp.src('src/css/tailwind.css')
.pipe(postcss([
tailwindcss('./tailwind.config.js'),
autoprefixer(),
...(process.env.NODE_ENV === 'production' ? [cssnano()] : [])
]))
.pipe(gulp.dest('dist/css/'));
}
CSS Optimization¶
const purgecss = require('gulp-purgecss');
const critical = require('critical');
// Remove unused CSS
function purgeCSS() {
return gulp.src('dist/css/**/*.css')
.pipe(purgecss({
content: ['dist/**/*.html', 'dist/**/*.js'],
safelist: ['active', 'show', /^carousel-/]
}))
.pipe(gulp.dest('dist/css/'));
}
// Extract critical CSS
function extractCritical() {
return critical.generate({
base: 'dist/',
src: 'index.html',
dest: 'css/critical.css',
inline: true,
width: 1300,
height: 900
});
}
// CSS linting
const stylelint = require('gulp-stylelint');
function lintCSS() {
return gulp.src('src/scss/**/*.scss')
.pipe(stylelint({
reporters: [
{ formatter: 'string', console: true }
]
}));
}
JavaScript Processing¶
Basic JavaScript Processing¶
const uglify = require('gulp-uglify');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
// Basic JavaScript minification
function minifyJS() {
return gulp.src('src/js/**/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist/js/'));
}
// Babel transpilation
function transpileJS() {
return gulp.src('src/js/**/*.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(gulp.dest('dist/js/'));
}
// Complete JavaScript pipeline
function processJS() {
return gulp.src('src/js/**/*.js')
.pipe(sourcemaps.init())
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(concat('bundle.js'))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/js/'))
.pipe(uglify())
.pipe(rename({ suffix: '.min' }))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/js/'));
}
TypeScript Processing¶
const typescript = require('gulp-typescript');
// TypeScript compilation
function compileTypeScript() {
const tsProject = typescript.createProject('tsconfig.json');
return tsProject.src()
.pipe(tsProject())
.js
.pipe(gulp.dest('dist/js/'));
}
// TypeScript with source maps
function buildTypeScript() {
const tsProject = typescript.createProject('tsconfig.json');
return gulp.src('src/ts/**/*.ts')
.pipe(sourcemaps.init())
.pipe(tsProject())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/js/'));
}
JavaScript Bundling¶
const browserify = require('browserify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
// Browserify bundling
function bundleJS() {
return browserify({
entries: 'src/js/main.js',
debug: true
})
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('dist/js/'));
}
// Webpack integration
const webpack = require('webpack-stream');
function webpackBundle() {
return gulp.src('src/js/main.js')
.pipe(webpack({
mode: 'production',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}))
.pipe(gulp.dest('dist/js/'));
}
JavaScript Linting¶
const eslint = require('gulp-eslint');
const jshint = require('gulp-jshint');
// ESLint
function lintJS() {
return gulp.src('src/js/**/*.js')
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
}
// JSHint
function hintJS() {
return gulp.src('src/js/**/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
}
// Combined linting
function checkJS() {
return gulp.src('src/js/**/*.js')
.pipe(eslint())
.pipe(eslint.format())
.pipe(jshint())
.pipe(jshint.reporter('default'));
}
Image Optimization¶
Basic Image Processing¶
const imagemin = require('gulp-imagemin');
const webp = require('gulp-webp');
const responsive = require('gulp-responsive');
// Basic image optimization
function optimizeImages() {
return gulp.src('src/images/**/*')
.pipe(imagemin([
imagemin.gifsicle({ interlaced: true }),
imagemin.mozjpeg({ quality: 75, progressive: true }),
imagemin.optipng({ optimizationLevel: 5 }),
imagemin.svgo({
plugins: [
{ removeViewBox: true },
{ cleanupIDs: false }
]
})
]))
.pipe(gulp.dest('dist/images/'));
}
// WebP conversion
function generateWebP() {
return gulp.src('src/images/**/*.{jpg,jpeg,png}')
.pipe(webp())
.pipe(gulp.dest('dist/images/webp/'));
}
// Responsive images
function createResponsiveImages() {
return gulp.src('src/images/**/*.{jpg,jpeg,png}')
.pipe(responsive({
'**/*': [
{
width: 320,
rename: { suffix: '-small' }
},
{
width: 640,
rename: { suffix: '-medium' }
},
{
width: 1024,
rename: { suffix: '-large' }
},
{
width: 1920,
rename: { suffix: '-xlarge' }
}
]
}))
.pipe(gulp.dest('dist/images/responsive/'));
}
Advanced Image Processing¶
const sharp = require('gulp-sharp-responsive');
const sprite = require('gulp.spritesmith');
// Sharp image processing
function processWithSharp() {
return gulp.src('src/images/**/*.{jpg,jpeg,png}')
.pipe(sharp({
formats: [
{ format: 'jpeg', options: { quality: 80 } },
{ format: 'webp', options: { quality: 80 } }
]
}))
.pipe(gulp.dest('dist/images/'));
}
// CSS Sprites
function createSprites() {
const spriteData = gulp.src('src/images/icons/*.png')
.pipe(sprite({
imgName: 'sprite.png',
cssName: 'sprite.css',
imgPath: '../images/sprite.png'
}));
spriteData.img.pipe(gulp.dest('dist/images/'));
spriteData.css.pipe(gulp.dest('dist/css/'));
return spriteData;
}
// SVG sprites
const svgSprite = require('gulp-svg-sprite');
function createSVGSprites() {
return gulp.src('src/images/icons/*.svg')
.pipe(svgSprite({
mode: {
symbol: {
dest: '.',
sprite: 'sprite.svg'
}
}
}))
.pipe(gulp.dest('dist/images/'));
}
HTML Processing¶
HTML Minification¶
const htmlmin = require('gulp-htmlmin');
const fileinclude = require('gulp-file-include');
const replace = require('gulp-replace');
// Basic HTML minification
function minifyHTML() {
return gulp.src('src/**/*.html')
.pipe(htmlmin({
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}))
.pipe(gulp.dest('dist/'));
}
// HTML includes
function processHTML() {
return gulp.src('src/pages/*.html')
.pipe(fileinclude({
prefix: '@@',
basepath: '@file'
}))
.pipe(gulp.dest('dist/'));
}
// HTML templating
function buildHTML() {
return gulp.src('src/templates/*.html')
.pipe(replace('{{TITLE}}', 'My Website'))
.pipe(replace('{{VERSION}}', process.env.npm_package_version))
.pipe(htmlmin({
collapseWhitespace: true,
removeComments: true
}))
.pipe(gulp.dest('dist/'));
}
HTML Validation and Processing¶
const htmlhint = require('gulp-htmlhint');
const w3cjs = require('gulp-w3cjs');
const inject = require('gulp-inject');
// HTML validation
function validateHTML() {
return gulp.src('dist/**/*.html')
.pipe(htmlhint())
.pipe(htmlhint.reporter())
.pipe(w3cjs())
.pipe(w3cjs.reporter());
}
// Asset injection
function injectAssets() {
const target = gulp.src('src/index.html');
const sources = gulp.src(['dist/js/**/*.js', 'dist/css/**/*.css'], { read: false });
return target
.pipe(inject(sources, {
relative: true,
transform: function (filepath) {
if (filepath.slice(-3) === '.js') {
return '<script src="' + filepath + '"></script>';
}
return '<link rel="stylesheet" href="' + filepath + '">';
}
}))
.pipe(gulp.dest('dist/'));
}
Development Server¶
Basic Server Setup¶
const browserSync = require('browser-sync').create();
const connect = require('gulp-connect');
// Browser Sync server
function serve() {
browserSync.init({
server: {
baseDir: 'dist'
},
port: 3000,
open: true
});
}
// Connect server
function connectServer() {
connect.server({
root: 'dist',
livereload: true,
port: 8080
});
}
// Reload browser
function reload(done) {
browserSync.reload();
done();
}
Advanced Server Configuration¶
// Advanced Browser Sync setup
function advancedServe() {
browserSync.init({
server: {
baseDir: 'dist',
middleware: [
// Custom middleware
function(req, res, next) {
console.log('Request:', req.url);
next();
}
]
},
port: 3000,
ui: {
port: 3001
},
files: [
'dist/**/*.html',
'dist/**/*.css',
'dist/**/*.js'
],
reloadDelay: 1000,
notify: false,
ghostMode: {
clicks: true,
forms: true,
scroll: true
}
});
}
// Proxy server
function proxyServe() {
browserSync.init({
proxy: 'localhost:8080',
port: 3000,
files: ['dist/**/*'],
reloadDelay: 1000
});
}
Live Reload¶
// Live reload with gulp-connect
function liveReload() {
return gulp.src('dist/**/*.html')
.pipe(connect.reload());
}
// Watch with live reload
function watchWithReload() {
gulp.watch('src/scss/**/*.scss', gulp.series(styles, reload));
gulp.watch('src/js/**/*.js', gulp.series(scripts, reload));
gulp.watch('src/**/*.html', gulp.series(copyHTML, reload));
}
// Complete development server
function dev() {
serve();
watchWithReload();
}
exports.serve = serve;
exports.dev = dev;
Watch Tasks¶
Basic Watch Setup¶
// Basic watch
function watch() {
gulp.watch('src/scss/**/*.scss', styles);
gulp.watch('src/js/**/*.js', scripts);
gulp.watch('src/**/*.html', copyHTML);
gulp.watch('src/images/**/*', images);
}
// Watch with options
function watchAdvanced() {
gulp.watch('src/scss/**/*.scss', {
events: 'all',
delay: 500
}, styles);
gulp.watch('src/js/**/*.js', {
ignoreInitial: false
}, scripts);
}
Advanced Watch Patterns¶
const chokidar = require('chokidar');
// Custom watcher
function customWatch() {
// Watch SCSS files
const scssWatcher = chokidar.watch('src/scss/**/*.scss');
scssWatcher.on('change', () => {
console.log('SCSS file changed');
styles();
});
// Watch JS files
const jsWatcher = chokidar.watch('src/js/**/*.js');
jsWatcher.on('add', () => {
console.log('JS file added');
scripts();
});
jsWatcher.on('unlink', () => {
console.log('JS file removed');
scripts();
});
}
// Conditional watching
function conditionalWatch() {
if (process.env.NODE_ENV === 'development') {
gulp.watch('src/scss/**/*.scss', gulp.series(styles, reload));
gulp.watch('src/js/**/*.js', gulp.series(lintJS, scripts, reload));
} else {
gulp.watch('src/scss/**/*.scss', styles);
gulp.watch('src/js/**/*.js', scripts);
}
}
Watch with Error Handling¶
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
// Error handling in watch
function watchWithErrors() {
gulp.watch('src/scss/**/*.scss', () => {
return gulp.src('src/scss/**/*.scss')
.pipe(plumber({
errorHandler: notify.onError({
title: 'Sass Error',
message: '<%= error.message %>'
})
}))
.pipe(sass())
.pipe(gulp.dest('dist/css/'))
.pipe(browserSync.stream());
});
}
// Graceful error handling
function gracefulWatch() {
gulp.watch('src/js/**/*.js', () => {
return gulp.src('src/js/**/*.js')
.pipe(plumber())
.pipe(babel())
.on('error', function(err) {
console.error('Error:', err.message);
this.emit('end');
})
.pipe(uglify())
.pipe(gulp.dest('dist/js/'));
});
}
Build Pipeline¶
Development Build¶
// Development build pipeline
const developmentBuild = gulp.series(
clean,
gulp.parallel(
copyHTML,
gulp.series(lintCSS, styles),
gulp.series(lintJS, scripts),
copyAssets,
optimizeImages
)
);
// Development with watch
const dev = gulp.series(
developmentBuild,
gulp.parallel(serve, watch)
);
exports.dev = dev;
Production Build¶
// Production build pipeline
const productionBuild = gulp.series(
clean,
gulp.parallel(
minifyHTML,
buildProductionCSS,
buildProductionJS,
optimizeImages,
copyAssets
),
injectAssets,
generateSitemap
);
// Production JavaScript build
function buildProductionJS() {
return gulp.src('src/js/**/*.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(concat('bundle.js'))
.pipe(uglify())
.pipe(rev())
.pipe(gulp.dest('dist/js/'))
.pipe(rev.manifest())
.pipe(gulp.dest('dist/'));
}
exports.build = productionBuild;
Multi-Environment Builds¶
const gulpif = require('gulp-if');
// Environment-aware build
function buildForEnvironment() {
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = process.env.NODE_ENV === 'development';
return gulp.src('src/js/**/*.js')
.pipe(gulpif(isDevelopment, sourcemaps.init()))
.pipe(babel())
.pipe(gulpif(isProduction, uglify()))
.pipe(gulpif(isDevelopment, sourcemaps.write('.')))
.pipe(gulp.dest('dist/js/'));
}
// Multiple environment configs
const environments = {
development: {
sourcemaps: true,
minify: false,
baseUrl: 'http://localhost:3000'
},
staging: {
sourcemaps: true,
minify: true,
baseUrl: 'https://staging.example.com'
},
production: {
sourcemaps: false,
minify: true,
baseUrl: 'https://example.com'
}
};
function buildWithConfig() {
const env = process.env.NODE_ENV || 'development';
const config = environments[env];
return gulp.src('src/**/*.js')
.pipe(gulpif(config.sourcemaps, sourcemaps.init()))
.pipe(replace('{{BASE_URL}}', config.baseUrl))
.pipe(gulpif(config.minify, uglify()))
.pipe(gulpif(config.sourcemaps, sourcemaps.write('.')))
.pipe(gulp.dest('dist/js/'));
}
Plugin Ecosystem¶
Essential Plugins¶
# Core plugins
npm install --save-dev gulp-sass gulp-autoprefixer gulp-clean-css
npm install --save-dev gulp-uglify gulp-concat gulp-babel
npm install --save-dev gulp-imagemin gulp-htmlmin
# Utility plugins
npm install --save-dev gulp-sourcemaps gulp-rename gulp-if
npm install --save-dev gulp-plumber gulp-notify gulp-filter
# Development plugins
npm install --save-dev browser-sync gulp-connect
npm install --save-dev gulp-eslint gulp-stylelint
# Advanced plugins
npm install --save-dev gulp-rev gulp-inject gulp-replace
npm install --save-dev gulp-zip gulp-ftp gulp-s3-upload
File Processing Plugins¶
// File manipulation
const rev = require('gulp-rev');
const revReplace = require('gulp-rev-replace');
const size = require('gulp-size');
// Asset revisioning
function revisionAssets() {
return gulp.src(['dist/**/*.css', 'dist/**/*.js'])
.pipe(rev())
.pipe(gulp.dest('dist/'))
.pipe(rev.manifest())
.pipe(gulp.dest('dist/'));
}
// Replace revisioned assets
function replaceRevisionedAssets() {
const manifest = gulp.src('dist/rev-manifest.json');
return gulp.src('dist/**/*.html')
.pipe(revReplace({ manifest: manifest }))
.pipe(gulp.dest('dist/'));
}
// File size reporting
function reportSizes() {
return gulp.src('dist/**/*')
.pipe(size({
showFiles: true,
gzip: true
}));
}
Testing Plugins¶
const mocha = require('gulp-mocha');
const karma = require('karma').Server;
const jest = require('gulp-jest');
// Mocha tests
function runMochaTests() {
return gulp.src('test/**/*.js', { read: false })
.pipe(mocha({
reporter: 'spec'
}));
}
// Karma tests
function runKarmaTests(done) {
new karma({
configFile: __dirname + '/karma.conf.js',
singleRun: true
}, done).start();
}
// Jest tests
function runJestTests() {
return gulp.src('.')
.pipe(jest({
preprocessorIgnorePatterns: [
'<rootDir>/dist/', '<rootDir>/node_modules/'
],
automock: false
}));
}
Deployment Plugins¶
const ftp = require('vinyl-ftp');
const s3 = require('gulp-s3-upload')();
const ghPages = require('gulp-gh-pages');
// FTP deployment
function deployFTP() {
const conn = ftp.create({
host: 'ftp.example.com',
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
parallel: 10
});
return gulp.src('dist/**/*', { base: 'dist', buffer: false })
.pipe(conn.newer('/public_html'))
.pipe(conn.dest('/public_html'));
}
// S3 deployment
function deployS3() {
return gulp.src('dist/**/*')
.pipe(s3({
Bucket: 'my-bucket',
ACL: 'public-read'
}));
}
// GitHub Pages deployment
function deployGitHub() {
return gulp.src('dist/**/*')
.pipe(ghPages());
}
Advanced Patterns¶
Custom Plugins¶
const through = require('through2');
const PluginError = require('plugin-error');
// Simple custom plugin
function customPlugin(options) {
options = options || {};
return through.obj(function(file, encoding, callback) {
if (file.isNull()) {
return callback(null, file);
}
if (file.isStream()) {
return callback(new PluginError('custom-plugin', 'Streaming not supported'));
}
try {
// Process file content
const content = file.contents.toString();
const processed = content.replace(/foo/g, 'bar');
file.contents = Buffer.from(processed);
this.push(file);
callback();
} catch (err) {
callback(new PluginError('custom-plugin', err));
}
});
}
// Usage
function useCustomPlugin() {
return gulp.src('src/**/*.js')
.pipe(customPlugin({ option: 'value' }))
.pipe(gulp.dest('dist/'));
}
Stream Processing¶
const map = require('map-stream');
const transform = require('stream-transform');
// Map stream processing
function processWithMap() {
return gulp.src('src/**/*.js')
.pipe(map((file, callback) => {
// Process each file
const content = file.contents.toString();
const processed = content.toUpperCase();
file.contents = Buffer.from(processed);
callback(null, file);
}))
.pipe(gulp.dest('dist/'));
}
// Transform stream
function processWithTransform() {
return gulp.src('src/**/*.json')
.pipe(transform((file, encoding, callback) => {
try {
const data = JSON.parse(file.contents.toString());
data.processed = true;
data.timestamp = new Date().toISOString();
file.contents = Buffer.from(JSON.stringify(data, null, 2));
callback(null, file);
} catch (err) {
callback(err);
}
}))
.pipe(gulp.dest('dist/'));
}
Conditional Processing¶
const yargs = require('yargs');
const gulpif = require('gulp-if');
// Command line arguments
const argv = yargs.argv;
function conditionalBuild() {
return gulp.src('src/**/*.js')
.pipe(gulpif(argv.lint, eslint()))
.pipe(gulpif(argv.babel, babel()))
.pipe(gulpif(argv.minify, uglify()))
.pipe(gulp.dest('dist/'));
}
// Environment-based conditions
function environmentalBuild() {
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction;
return gulp.src('src/**/*.js')
.pipe(gulpif(isDevelopment, sourcemaps.init()))
.pipe(babel())
.pipe(gulpif(isProduction, uglify()))
.pipe(gulpif(isDevelopment, sourcemaps.write('.')))
.pipe(gulp.dest('dist/'));
}
// File-based conditions
function fileBasedProcessing() {
return gulp.src('src/**/*')
.pipe(gulpif('*.js', uglify()))
.pipe(gulpif('*.css', cleanCSS()))
.pipe(gulpif('*.html', htmlmin()))
.pipe(gulp.dest('dist/'));
}
Performance Optimization¶
Parallel Processing¶
// Parallel task execution
const parallelBuild = gulp.parallel(
styles,
scripts,
images,
copyAssets
);
// Mixed series and parallel
const optimizedBuild = gulp.series(
clean,
gulp.parallel(
gulp.series(lintCSS, styles),
gulp.series(lintJS, scripts),
images
),
injectAssets
);
exports.build = optimizedBuild;
Incremental Builds¶
const changed = require('gulp-changed');
const newer = require('gulp-newer');
// Process only changed files
function incrementalStyles() {
return gulp.src('src/scss/**/*.scss')
.pipe(changed('dist/css/', { extension: '.css' }))
.pipe(sass())
.pipe(gulp.dest('dist/css/'));
}
// Process only newer files
function incrementalImages() {
return gulp.src('src/images/**/*')
.pipe(newer('dist/images/'))
.pipe(imagemin())
.pipe(gulp.dest('dist/images/'));
}
// Cache-based processing
const cache = require('gulp-cache');
function cachedImages() {
return gulp.src('src/images/**/*')
.pipe(cache(imagemin()))
.pipe(gulp.dest('dist/images/'));
}
Memory Optimization¶
const remember = require('gulp-remember');
const cached = require('gulp-cached');
// Cached processing with remember
function optimizedScripts() {
return gulp.src('src/js/**/*.js')
.pipe(cached('scripts'))
.pipe(eslint())
.pipe(remember('scripts'))
.pipe(concat('bundle.js'))
.pipe(gulp.dest('dist/js/'));
}
// Clear cache when needed
function clearCache() {
return cache.clearAll();
}
// Memory-efficient streaming
function streamEfficient() {
return gulp.src('src/**/*.js', { buffer: false })
.pipe(someTransform())
.pipe(gulp.dest('dist/'));
}
Error Handling¶
Basic Error Handling¶
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
// Plumber for error handling
function errorHandledStyles() {
return gulp.src('src/scss/**/*.scss')
.pipe(plumber({
errorHandler: function(err) {
console.error('Error:', err.message);
this.emit('end');
}
}))
.pipe(sass())
.pipe(gulp.dest('dist/css/'));
}
// Notify on errors
function notifyOnError() {
return gulp.src('src/scss/**/*.scss')
.pipe(plumber({
errorHandler: notify.onError({
title: 'Sass Error',
message: '<%= error.message %>'
})
}))
.pipe(sass())
.pipe(gulp.dest('dist/css/'));
}
Advanced Error Handling¶
// Custom error handler
function createErrorHandler(taskName) {
return function(err) {
console.error(`Error in ${taskName}:`, err.message);
if (err.stack) {
console.error(err.stack);
}
this.emit('end');
};
}
// Error handling with logging
const log = require('fancy-log');
const colors = require('ansi-colors');
function advancedErrorHandling() {
return gulp.src('src/js/**/*.js')
.pipe(plumber({
errorHandler: function(err) {
log.error(colors.red('JavaScript Error:'));
log.error(colors.red(err.message));
if (err.fileName) {
log.error(colors.yellow('File:'), err.fileName);
}
if (err.lineNumber) {
log.error(colors.yellow('Line:'), err.lineNumber);
}
this.emit('end');
}
}))
.pipe(babel())
.pipe(gulp.dest('dist/js/'));
}
// Graceful degradation
function gracefulBuild() {
return gulp.src('src/**/*.js')
.pipe(plumber())
.pipe(babel())
.on('error', function(err) {
log.error('Babel compilation failed, using original files');
return gulp.src('src/**/*.js')
.pipe(gulp.dest('dist/js/'));
})
.pipe(uglify())
.pipe(gulp.dest('dist/js/'));
}
Testing¶
Unit Testing¶
const mocha = require('gulp-mocha');
const istanbul = require('gulp-istanbul');
// Run Mocha tests
function runTests() {
return gulp.src('test/**/*.js', { read: false })
.pipe(mocha({
reporter: 'spec',
timeout: 5000
}));
}
// Code coverage
function coverage() {
return gulp.src('src/**/*.js')
.pipe(istanbul())
.pipe(istanbul.hookRequire());
}
function testWithCoverage() {
return gulp.src('test/**/*.js', { read: false })
.pipe(mocha())
.pipe(istanbul.writeReports())
.pipe(istanbul.enforceThresholds({ thresholds: { global: 90 } }));
}
const testSuite = gulp.series(coverage, testWithCoverage);
exports.test = testSuite;
Integration Testing¶
const puppeteer = require('puppeteer');
const assert = require('assert');
// Browser testing
async function browserTest() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:3000');
const title = await page.title();
assert.strictEqual(title, 'My Website');
await browser.close();
}
// Visual regression testing
const backstop = require('backstopjs');
function visualTest() {
return backstop('test', {
config: 'backstop.json'
});
}
// Performance testing
const lighthouse = require('lighthouse');
async function performanceTest() {
const results = await lighthouse('http://localhost:3000', {
output: 'json',
onlyCategories: ['performance']
});
const score = results.lhr.categories.performance.score * 100;
console.log('Performance Score:', score);
if (score < 90) {
throw new Error('Performance score too low');
}
}
Deployment¶
Build and Deploy Pipeline¶
const zip = require('gulp-zip');
const ftp = require('vinyl-ftp');
// Create deployment package
function createPackage() {
return gulp.src('dist/**/*')
.pipe(zip('deploy.zip'))
.pipe(gulp.dest('packages/'));
}
// Deploy via FTP
function deployToServer() {
const conn = ftp.create({
host: process.env.FTP_HOST,
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
parallel: 10,
log: console.log
});
return gulp.src('dist/**/*', { base: 'dist', buffer: false })
.pipe(conn.newer('/public_html'))
.pipe(conn.dest('/public_html'));
}
// Complete deployment pipeline
const deploy = gulp.series(
productionBuild,
runTests,
createPackage,
deployToServer
);
exports.deploy = deploy;
CI/CD Integration¶
// Environment-aware deployment
function deployToEnvironment() {
const env = process.env.NODE_ENV || 'development';
const configs = {
development: {
host: 'dev.example.com',
path: '/dev'
},
staging: {
host: 'staging.example.com',
path: '/staging'
},
production: {
host: 'example.com',
path: '/public_html'
}
};
const config = configs[env];
return gulp.src('dist/**/*')
.pipe(deployTo(config));
}
// Docker deployment
function buildDockerImage() {
return gulp.src(['dist/**/*', 'Dockerfile'])
.pipe(docker.build({
tag: 'my-app:latest'
}));
}
// Kubernetes deployment
function deployToK8s() {
return gulp.src('k8s/*.yaml')
.pipe(kubectl.apply());
}
Best Practices¶
Project Organization¶
// Organized gulpfile structure
const { src, dest, watch, series, parallel } = require('gulp');
// Task modules
const styles = require('./gulp-tasks/styles');
const scripts = require('./gulp-tasks/scripts');
const images = require('./gulp-tasks/images');
const server = require('./gulp-tasks/server');
// Configuration
const config = require('./gulp.config.js');
// Export tasks
exports.styles = styles;
exports.scripts = scripts;
exports.images = images;
exports.serve = server.serve;
exports.build = parallel(styles, scripts, images);
exports.dev = series(exports.build, server.serve, server.watch);
Configuration Management¶
// gulp.config.js
module.exports = {
paths: {
src: 'src',
dest: 'dist',
styles: {
src: 'src/scss/**/*.scss',
dest: 'dist/css/'
},
scripts: {
src: 'src/js/**/*.js',
dest: 'dist/js/'
},
images: {
src: 'src/images/**/*',
dest: 'dist/images/'
}
},
options: {
sass: {
outputStyle: 'compressed',
includePaths: ['node_modules']
},
autoprefixer: {
browsers: ['last 2 versions', '> 1%']
},
imagemin: {
optimizationLevel: 5,
progressive: true,
interlaced: true
}
},
server: {
port: 3000,
baseDir: 'dist'
}
};
Performance Best Practices¶
- Use incremental builds with
gulp-changed
orgulp-newer
- Implement parallel processing for independent tasks
- Cache expensive operations with
gulp-cache
- Use streaming instead of buffering for large files
- Optimize watch patterns to avoid unnecessary rebuilds
Code Quality Best Practices¶
- Implement linting for CSS, JavaScript, and HTML
- Use source maps for debugging in development
- Add error handling with
gulp-plumber
- Include testing in your build pipeline
- Document your tasks and configuration
Maintenance Best Practices¶
- Keep plugins updated regularly
- Use semantic versioning for your builds
- Implement proper logging for debugging
- Create modular task files for better organization
- Use environment variables for sensitive configuration
Summary¶
Gulp is a powerful streaming build system that excels at automating repetitive tasks in web development. Key features include:
- Stream-based Processing: Efficient file processing without temporary files
- Code over Configuration: JavaScript-based configuration for maximum flexibility
- Rich Plugin Ecosystem: Thousands of plugins for every conceivable task
- Parallel Processing: Run tasks simultaneously for faster builds
- Watch and Live Reload: Automatic rebuilds and browser refresh
- Incremental Builds: Process only changed files for speed
- Error Handling: Robust error handling to prevent build failures
- Extensible: Easy to create custom plugins and tasks
By leveraging Gulp's streaming architecture and extensive plugin ecosystem, you can create efficient, maintainable build processes that scale with your project's complexity.