Skip to content

Instantly share code, notes, and snippets.

@Vintharas
Last active July 17, 2018 07:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Vintharas/9999cf6098c66e81eb7736db7b779951 to your computer and use it in GitHub Desktop.
Save Vintharas/9999cf6098c66e81eb7736db7b779951 to your computer and use it in GitHub Desktop.
Sample gulpfile for a complete pipeline with ES6 and SASS
/* eslint strict: [2, "global"] */
'use strict';
/*** Main paths ***/
var projectRoot = './',
root = './myProject.web/', // project root
index = root + 'Views/Shared/Layouts/',
src = root + 'Content/', // source code
temp = root + 'temp/', // temporary folder
dist = src + 'dist/', // generated code
templates = src + 'templates/', // html templates for angular
bower = { // third parties
directory: root + 'bower_components/',
json: './bower.json',
},
packages = {
json: './package.json'
};
/*** Subpaths for reuse ***/
var bowerFontsSrc = [bower.directory + 'bootstrap-sass/assets/fonts/**/*.*', bower.directory + 'font-awesome/fonts/*.*'];
/* API */
exports.config = getConfig();
function getConfig() {
var config = {
projectRoot: projectRoot,
root: root,
src: src,
dist: dist,
temp: temp,
packages: [bower.json, packages.json],
/*** CSS ***/
sass: root + 'Styles/styles.scss',
allSass: [root + 'Styles/**/*.scss'],
distCss: dist + 'css/',
distCssFiles: dist + 'css/**/*.css',
/*** JS ***/
js: src + 'js/**/*.js',
tempJs: temp + '**/*.js',
jsConfigs: root + '../*.js',
specs: src + 'specs/**/*.spec.js',
distJs: dist + 'js/',
distJsFiles: dist + 'js/**/*.js',
/*** HTML ***/
index: index,
indexOriginFile: index + '_RootChrome.orig.cshtml',
indexGeneratedFileRename: './Views/Shared/Layouts/_RootChrome.cshtml',
indexGeneratedFile: index + '_RootChrome.cshtml',
indexGeneratedFileName: './_RootChrome.cshtml',
htmlTemplates: templates + '**/*.html',
/*** Fonts ***/
bowerFontsSrc: bowerFontsSrc,
allFontsSrc: bowerFontsSrc.concat([root + 'Content/fonts/*.*']),
fonts: root + 'Content/fonts/',
fontsDist: dist + 'fonts/',
/*** Resources ***/
resources: root + 'Content/resources/locale-*.json',
resourcesDist: dist + 'resources/',
get allJs() {
return [this.js, this.jsConfigs, this.specs];
},
/*** wiredep ***/
// wire dep needs to know about bower directories so that
// it can find the dependencies and inject them in our index.html file
getWiredepOptions: function (logger) {
return {
exclude: ['bower_components/jquery'],
bowerJson: require(bower.json),
directory: bower.directory,
ignorePath: '../../..',
onError: function (err) {
logger.log('error when running wiredep: ' + err + ' with code ' + err.code);
// If not overridden, an error will throw.
// err = Error object.
// err.code can be:
// - "PKG_NOT_INSTALLED" (a Bower package was not found)
// - "BOWER_COMPONENTS_MISSING" (cannot find the `bower_components` directory)
},
onFileUpdated: function (filePath) {
// filePath = 'name-of-file-that-was-updated'
logger.log('file updated: ' + filePath);
},
onPathInjected: function (fileObject) {
// fileObject.block = 'type-of-wiredep-block' ('js', 'css', etc)
// fileObject.file = 'name-of-file-that-was-updated'
// fileObject.path = 'path-to-file-that-was-injected'
logger.log('file injected: ' + fileObject.path);
},
onMainNotFound: function (pkg) {
// pkg = 'name-of-bower-package-without-main'
logger.error('main not found for package: ' + pkg);
}
};
},
/*** inject ***/
injectOptions: {
ignorePath: '/myProject.se.web'
},
/*** rev ***/
manifest: temp,
manifestFile: './rev-manifest.json',
manifestFileFullPath: temp + './rev-manifest.json'
};
return config;
}
/* eslint strict: 0 */
'use strict';
var gulp = require('gulp'),
config = require('./gulp.config.js').config,
del = require('del'),
$ = require('gulp-load-plugins')({ lazy: true }),
path = require('path'),
exec = require('child_process').exec,
args = require('yargs').argv,
ngAnnotate = require('gulp-ng-annotate');
/* Gulp tasks */
gulp.task('help', $.taskListing);
gulp.task('js:clean', function(){
log('Cleaning generated javascript files');
var jsFilesInDist = path.join(config.distJs, '**/*.*'),
tempFiles = path.join(config.temp, '**/*.*'),
files = [jsFilesInDist, tempFiles];
return cleanFiles(files);
});
gulp.task('clean', function(){
var filesInDist = path.join(config.dist, '**/*.*'),
tempFiles = path.join(config.temp, '**/*.*'),
files = [filesInDist, tempFiles];
return cleanFiles(files);
});
gulp.task('clean:temp', function(){
return cleanFiles(path.join(config.temp, '**/*.*'));
});
//gulp.task('default', function () {
// return gulp.src('Content/js')
// .pipe(ngAnnotate())
// .pipe(gulp.dest('dist'));
//});
gulp.task('fonts', function(){
return copyFiles(config.bowerFontsSrc, config.fonts);
});
gulp.task('fonts:dist', function(){
return copyFiles(config.allFontsSrc, config.fontsDist);
});
gulp.task('resources', function () {
return copyFiles(config.resources, config.resourcesDist);
});
gulp.task('resources:watch', ['resources'], function (done) {
// use of gulp-watch plugin instead of gulp.watch
// because it supports watching new or deleted files
log('Watching on resources changes');
$.watch(config.resources, function () {
gulp
.start('resources')
.on('end', done);
});
});
gulp.task('lint', function(){
log('Linting all javascript files');
return gulp.src(config.allJs)
.pipe($.print())
.pipe($.eslint())
.pipe($.eslint.format());
});
gulp.task('lint:watch', ['lint'], function(done){
log('Watching changes on all javascript files and linting');
// use of gulp-watch plugin instead of gulp.watch
// because it supports watching new or deleted files
$.watch(config.allJs, function () {
gulp
.start('lint')
.on('end', done);
});
});
gulp.task('test', function(done){
log('Running JavaScript unit tests with Karma');
startTests({singleRun: true, done: done});
});
gulp.task('test:watch', function(done) {
log('Running JavaScript unit tests with Karma and watch for changes');
startTests({singleRun: false, done: done});
});
gulp.task('testlint:watch', ['test:watch', 'lint:watch'], function() {});
function templateCache() {
var templatesFile = 'templates.js',
options = {
module: 'myapp',
standAlone: false, // add them to the existing myapp module (don't create a new module)
root: '/Content/templates/' // prefix for template urls
};
log('Starting bundling and minification of templates in: ' + config.htmlTemplates);
return gulp.src(config.htmlTemplates) // clientApp + '**/*.html'
.pipe($.minifyHtml({empty: true})) // include empty HTML tags
.pipe($.angularTemplatecache(templatesFile, options))
.pipe(gulp.dest(config.temp));
}
gulp.task('templates', ['clean'], templateCache);
gulp.task('templates:noclean', templateCache);
function js() {
log('Compiling ES6 to vanilla JavaScript');
log('Copying generated files into: ' + config.distJs);
return gulp.src([config.js, config.tempJs])
.pipe($.sourcemaps.init())
.pipe($.babel())
.pipe(ngAnnotate({ single_quotes: true }))
.pipe($.concat('my-app.js'))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest(config.distJs));
}
gulp.task('js', ['templates'], js);
gulp.task('js:noclean', js);
gulp.task('js:watch', ['js:noclean'], function(done){
// use of gulp-watch plugin instead of gulp.watch
// because it supports watching new or deleted files
log('Watching on js changes');
$.watch(config.js, function () {
gulp
.start('js:noclean')
.on('end', done);
});
});
function sass() {
log('Starting sass compilation of: ' + config.sass);
return gulp.src(config.sass)
.pipe($.plumber())
.pipe($.print())
.pipe($.sourcemaps.init())
.pipe($.sass().on('error', $.sass.logError))
.pipe($.autoprefixer({ browsers: ['last 2 version', '> 5%'] }))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest(config.distCss));
}
gulp.task('sass', ['clean'], sass);
gulp.task('sass:noclean', sass);
gulp.task('sass:watch', ['sass:noclean'], function(done){
// use of gulp-watch plugin instead of gulp.watch
// because it supports watching new or deleted files
log('Watching on sass styles changes');
return $.watch(config.allSass, function(){
gulp
.start('sass:noclean')
.on('end', done);
});
});
gulp.task('wiredep', ['js', 'sass', 'fonts:dist', 'resources'], function(){
/* wire bower css/js dependencies in our app js */
var wiredep = require('wiredep').stream,
wiredepOptions = config.getWiredepOptions(logger());
log('Starting wiring of dependencies into root cshtml file: ' + config.index);
// log('Reading: ' + JSON.stringify(wiredepOptions.bowerJson)); // debug
log('Bower components in folder: ' + wiredepOptions.directory);
return gulp
.src(config.indexOriginFile)
.pipe($.plumber())
.pipe($.rename(config.indexGeneratedFileName))
.pipe(wiredep(wiredepOptions))
.pipe($.inject(gulp.src(config.distJsFiles), config.injectOptions))
.pipe($.inject(gulp.src(config.distCssFiles), config.injectOptions))
.pipe(gulp.dest(config.index));
});
gulp.task('bundle', ['js', 'sass', 'fonts:dist', 'resources'], function () {
/* wire bower css/js dependencies in our app js */
var wiredep = require('wiredep').stream,
wiredepOptions = config.getWiredepOptions(logger());
log('Starting wiring of dependencies into root cshtml file: ' + config.indexOriginFile);
log('The generated file will be placed in: ' + config.indexGeneratedFileName);
//log('Reading: ' + JSON.stringify(wiredepOptions.bowerJson));
log('Bower components in folder: ' + wiredepOptions.directory);
return gulp
.src(config.indexOriginFile)
.pipe($.plumber())
.pipe($.rename(config.indexGeneratedFileName))
.pipe(wiredep(wiredepOptions))
.pipe($.inject(gulp.src(config.distJsFiles), config.injectOptions))
.pipe($.inject(gulp.src(config.distCssFiles), config.injectOptions))
.pipe($.useref({searchPath: config.root, base: config.root}))
.pipe(gulp.dest(config.index));
});
// This task must be run together with cache busting
// TODO: improve!
gulp.task('optimize', ['js', 'sass', 'fonts:dist', 'resources'], function () {
/* wire bower css/js dependencies in our app js */
var wiredep = require('wiredep').stream,
wiredepOptions = config.getWiredepOptions(logger());
log('Starting bundling and optimizing dependencies and bundling them into root cshtml file: ' + config.indexOriginFile);
log('The generated file will be placed in: ' + config.indexGeneratedFileName);
log('Bower components in folder: ' + wiredepOptions.directory);
return gulp
.src(config.indexOriginFile, {base: config.root})
.pipe($.print())
.pipe($.plumber())
.pipe($.rename(config.indexGeneratedFileName))
.pipe($.print())
.pipe(wiredep(wiredepOptions))
.pipe($.inject(gulp.src(config.distJsFiles), config.injectOptions))
.pipe($.inject(gulp.src(config.distCssFiles), config.injectOptions))
.pipe($.useref({searchPath: config.root, base: config.root}))
.pipe($.if('*.css', $.csso()))
.pipe($.if('*.js', $.uglify())) // TODO: Optimize this to grab the already minified versions of vendor libraries
.pipe($.if('*.css', $.rev()))
.pipe($.if('*.js', $.rev()))
.pipe($.if('*.css', gulp.dest(config.root)))
.pipe($.if('*.js', gulp.dest(config.root)))
.pipe($.print())
.pipe($.if('*.cshtml', gulp.dest(config.index)))
.pipe($.rev.manifest(config.manifestFile))
.pipe(gulp.dest(config.manifest));
});
gulp.task('cachebusting', ['optimize'], function(){
log('Injected cache busting dependencies in: ' + config.indexGeneratedFile);
return gulp
.src(config.indexGeneratedFile)
.pipe($.print())
.pipe($.revReplace({replaceInExtensions: ['.cshtml', '.js', '.css'], manifest: gulp.src(config.manifestFileFullPath)}))
.pipe($.print())
.pipe(gulp.dest(config.index));
});
gulp.task('bump', function(){
var options = {
type: args.type,
version: args.version
};
return gulp
.src(config.packages)
.pipe($.bump(options))
.pipe($.print())
.pipe(gulp.dest(config.projectRoot));
});
/*** CI only tasks ***/
gulp.task('bower', function (cb) {
exec('.\\tools\\CI\\bower.cmd install -f', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
cb(err);
});
});
// TODO: build more granular tasks for js and sass
// because the ones we have now are not wired
gulp.task('build', ['wiredep']); // => all separate dependencies
gulp.task('build:dev', ['wiredep']);
gulp.task('build:bundle', ['bundle']); // => all dependencies bundled
gulp.task('build:prod', ['cachebusting']); // => all dependencies optimized and with hash
gulp.task('watch', ['clean'], function() {
gulp.start(['lint:watch', 'js:watch', 'sass:watch', 'fonts:dist', 'resources:watch']);
});
gulp.task('default', ['help']);
/* Gulpfile helpers */
function log(message){
$.util.log($.util.colors.blue(message));
}
function error(message){
$.util.log($.util.colors.red(message));
}
function logger(){
return {
log: log,
error: error
};
}
function cleanFiles(files){
log('Cleaning files: ' + files);
return del(files);
}
function startTests(options){
var singleRun = options.singleRun,
done = options.done,
Server = require('karma').Server,
karmaOptions = {
configFile: path.join(__dirname, '/karma.conf.js'),
singleRun: singleRun
},
server = new Server(karmaOptions, karmaCompleted);
server.start();
function karmaCompleted(exitCode){
if (exitCode !== 0) {
var errorMessage = 'Karma exited with error code: ' + exitCode;
error(errorMessage);
return process.exit(exitCode);
}
done();
}
if (options.singleRun) {
gulp.src('karma_html/coverage-report/index.html')
.pipe($.open({ app: 'chrome' }));
gulp.src('karma_html/test-report/index.html')
.pipe($.open({ app: 'chrome' }));
}
}
function copyFiles(from, to) {
log('Copying files from ' + from + ' to folder: ' + to);
return gulp.src(from)
.pipe($.print())
.pipe(gulp.dest(to));
}
{
"name": "myapp",
"version": "1.0.1",
"description": "my app",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://myrepo.com"
},
"engine": {
"node": "0.12.x",
"npm": "2.13.x"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-preset-es2015": "^6.1.18",
"bower": "^1.7.2",
"del": "^2.0.2",
"eslint-config-eslint": "^1.0.1",
"gulp": "^3.9.0",
"gulp-angular-templatecache": "^1.8.0",
"gulp-autoprefixer": "^2.3.1",
"gulp-babel": "^5.2.1",
"gulp-bump": "^1.0.0",
"gulp-concat": "^2.6.0",
"gulp-csso": "^1.0.1",
"gulp-eslint": "^1.1.1",
"gulp-if": "^2.0.0",
"gulp-inject": "^3.0.0",
"gulp-load-plugins": "^0.10.0",
"gulp-minify-html": "^1.0.5",
"gulp-plumber": "^1.0.1",
"gulp-print": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-rev": "^6.0.1",
"gulp-rev-replace": "^0.4.3",
"gulp-sass": "^2.0.4",
"gulp-sourcemaps": "^1.5.2",
"gulp-task-listing": "^1.0.1",
"gulp-uglify": "^1.5.1",
"gulp-useref": "^3.0.5",
"gulp-util": "^3.0.6",
"gulp-watch": "^4.3.5",
"jasmine-core": "^2.3.4",
"karma": "^0.13.22",
"karma-babel-preprocessor": "^6.0.1",
"karma-chrome-launcher": "^0.2.0",
"karma-jasmine": "^0.3.6",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.4",
"sinon": "^1.17.1",
"wiredep": "^3.0.0",
"yargs": "^3.19.0",
"karma-html-reporter": "^0.2.7",
"karma-coverage": "^0.5.5",
"gulp-open": "^1.0.0",
"gulp-ng-annotate": "^2.0.0",
"phantomjs-prebuilt": "^2.1.7",
"karma-phantomjs2-launcher": "^0.5.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment