generator-angular-crud
Version:
Angular CRUD generator
688 lines (609 loc) • 19.4 kB
JavaScript
var args = require('yargs').argv;
var browserSync = require('browser-sync');
var config = require('./gulp.config')();
var del = require('del');
var glob = require('glob');
var gulp = require('gulp');
var path = require('path');
var _ = require('lodash');
var $ = require('gulp-load-plugins')({lazy: true});
var colors = $.util.colors;
var envenv = $.util.env;
var port = process.env.PORT || config.defaultPort;
/**
* yargs variables can be passed in to alter the behavior, when present.
* Example: gulp serve-dev
*
* --verbose : Various tasks will produce more output to the console.
* --nosync : Don't launch the browser with browser-sync when serving code.
* --debug : Launch debugger with node-inspector.
* --debug-brk: Launch debugger and break on 1st line with node-inspector.
* --startServers: Will start servers for midway tests on the test task.
*/
/**
* List the available gulp tasks
*/
gulp.task('help', $.taskListing);
gulp.task('default', ['help']);
/**
* vet the code and create coverage report
* @return {Stream}
*/
gulp.task('vet', function() {
log('Analyzing source with JSHint and JSCS');
return gulp
.src(config.alljs)
.pipe($.if(args.verbose, $.print()))
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
.pipe($.jshint.reporter('fail'))
.pipe($.jscs());
});
/**
* Create a visualizer report
*/
gulp.task('plato', function(done) {
log('Analyzing source with Plato');
log('Browse to /report/plato/index.html to see Plato results');
startPlatoVisualizer(done);
});
/**
* Compile less to css
* @return {Stream}
*/
gulp.task('styles', ['clean-styles'], function() {
log('Compiling Less --> CSS');
return gulp
.src(config.less)
.pipe($.plumber()) // exit gracefully if something fails after this
.pipe($.less())
// .on('error', errorLogger) // more verbose and dupe output. requires emit.
.pipe($.autoprefixer({browsers: ['last 2 version', '> 5%']}))
.pipe(gulp.dest(config.temp));
});
/**
* Copy fonts
* @return {Stream}
*/
gulp.task('fonts', ['clean-fonts'], function() {
log('Copying fonts');
return gulp
.src(config.fonts, {bower: config.bower.directory})
.pipe(gulp.dest(config.build + 'fonts'));
});
/**
* Compress images
* @return {Stream}
*/
gulp.task('images', ['clean-images'], function() {
log('Compressing and copying images');
return gulp
.src(config.images)
.pipe($.imagemin({optimizationLevel: 4}))
.pipe(gulp.dest(config.build + 'images'));
});
gulp.task('less-watcher', function() {
gulp.watch([config.less], ['styles']);
});
/**
* Create $templateCache from the html templates
* @return {Stream}
*/
gulp.task('templatecache', ['clean-code'], function() {
log('Creating an AngularJS $templateCache');
return gulp
.src(config.htmltemplates)
.pipe($.if(args.verbose, $.bytediff.start()))
.pipe($.minifyHtml({empty: true}))
.pipe($.if(args.verbose, $.bytediff.stop(bytediffFormatter)))
.pipe($.angularTemplatecache(
config.templateCache.file,
config.templateCache.options
))
.pipe(gulp.dest(config.temp));
});
/**
* Wire-up the bower dependencies
* @return {Stream}
*/
gulp.task('wiredep', function() {
log('Wiring the bower dependencies into the html');
var wiredep = require('wiredep').stream;
var options = config.getWiredepDefaultOptions();
// Only include stubs if flag is enabled
var js = args.stubs ? [].concat(config.js, config.stubsjs) : config.js;
return gulp
.src(config.index)
.pipe(wiredep(options))
.pipe(inject(js, '', config.jsOrder))
.pipe(gulp.dest(config.client));
});
gulp.task('inject', ['wiredep', 'styles', 'templatecache'], function() {
log('Wire up css into the html, after files are ready');
return gulp
.src(config.index)
.pipe(inject(config.css))
.pipe(gulp.dest(config.client));
});
/**
* Run the spec runner
* @return {Stream}
*/
gulp.task('serve-specs', ['build-specs'], function(done) {
log('run the spec runner');
serve(true /* isDev */, true /* specRunner */);
done();
});
/**
* Inject all the spec files into the specs.html
* @return {Stream}
*/
gulp.task('build-specs', ['templatecache'], function(done) {
log('building the spec runner');
var wiredep = require('wiredep').stream;
var templateCache = config.temp + config.templateCache.file;
var options = config.getWiredepDefaultOptions();
var specs = config.specs;
if (args.startServers) {
specs = [].concat(specs, config.serverIntegrationSpecs);
}
options.devDependencies = true;
return gulp
.src(config.specRunner)
.pipe(wiredep(options))
.pipe(inject(config.js, '', config.jsOrder))
.pipe(inject(config.testlibraries, 'testlibraries'))
.pipe(inject(config.specHelpers, 'spechelpers'))
.pipe(inject(specs, 'specs', ['**/*']))
.pipe(inject(templateCache, 'templates'))
.pipe(gulp.dest(config.client));
});
/**
* Build everything
* This is separate so we can run tests on
* optimize before handling image or fonts
*/
gulp.task('build', ['optimize', 'images', 'fonts'], function() {
log('Building everything');
var msg = {
title: 'gulp build',
subtitle: 'Deployed to the build folder',
message: 'Running `gulp serve-build`'
};
del(config.temp);
log(msg);
notify(msg);
});
/**
* Optimize all files, move to a build folder,
* and inject them into the new index.html
* @return {Stream}
*/
gulp.task('optimize', ['inject', 'vet'], function() {
log('Optimizing the js, css, and html');
var assets = $.useref.assets({searchPath: './'});
// Filters are named for the gulp-useref path
var cssFilter = $.filter('**/*.css');
var jsAppFilter = $.filter('**/' + config.optimized.app);
var jslibFilter = $.filter('**/' + config.optimized.lib);
var templateCache = config.temp + config.templateCache.file;
return gulp
.src(config.index)
.pipe($.plumber())
.pipe(inject(templateCache, 'templates'))
.pipe(assets) // Gather all assets from the html with useref
// Get the css
.pipe(cssFilter)
.pipe($.csso())
.pipe(cssFilter.restore())
// Get the custom javascript
.pipe(jsAppFilter)
.pipe($.ngAnnotate({add: true}))
.pipe($.uglify())
.pipe(getHeader())
.pipe(jsAppFilter.restore())
// Get the vendor javascript
.pipe(jslibFilter)
.pipe($.uglify()) // another option is to override wiredep to use min files
.pipe(jslibFilter.restore())
// Take inventory of the file names for future rev numbers
.pipe($.rev())
// Apply the concat and file replacement with useref
.pipe(assets.restore())
.pipe($.useref())
// Replace the file names in the html with rev numbers
.pipe($.revReplace())
.pipe(gulp.dest(config.build));
});
/**
* Remove all files from the build, temp, and reports folders
* @param {Function} done - callback when complete
*/
gulp.task('clean', function(done) {
var delconfig = [].concat(config.build, config.temp, config.report);
log('Cleaning: ' + $.util.colors.blue(delconfig));
del(delconfig, done);
});
/**
* Remove all fonts from the build folder
* @param {Function} done - callback when complete
*/
gulp.task('clean-fonts', function(done) {
clean(config.build + 'fonts/**/*.*', done);
});
/**
* Remove all images from the build folder
* @param {Function} done - callback when complete
*/
gulp.task('clean-images', function(done) {
clean(config.build + 'images/**/*.*', done);
});
/**
* Remove all styles from the build and temp folders
* @param {Function} done - callback when complete
*/
gulp.task('clean-styles', function(done) {
var files = [].concat(
config.temp + '**/*.css',
config.build + 'styles/**/*.css'
);
clean(files, done);
});
/**
* Remove all js and html from the build and temp folders
* @param {Function} done - callback when complete
*/
gulp.task('clean-code', function(done) {
var files = [].concat(
config.temp + '**/*.js',
config.build + 'js/**/*.js',
config.build + '**/*.html'
);
clean(files, done);
});
/**
* Run specs once and exit
* To start servers and run midway specs as well:
* gulp test --startServers
* @return {Stream}
*/
gulp.task('test', ['vet', 'templatecache'], function(done) {
startTests(true /*singleRun*/ , done);
});
/**
* Run specs and wait.
* Watch for file changes and re-run tests on each change
* To start servers and run midway specs as well:
* gulp autotest --startServers
*/
gulp.task('autotest', function(done) {
startTests(false /*singleRun*/ , done);
});
/**
* serve the dev environment
* --debug-brk or --debug
* --nosync
*/
gulp.task('serve-dev', ['inject'], function() {
serve(true /*isDev*/);
});
/**
* serve the build environment
* --debug-brk or --debug
* --nosync
*/
gulp.task('serve-build', ['build'], function() {
serve(false /*isDev*/);
});
/**
* Bump the version
* --type=pre will bump the prerelease version *.*.*-x
* --type=patch or no flag will bump the patch version *.*.x
* --type=minor will bump the minor version *.x.*
* --type=major will bump the major version x.*.*
* --version=1.2.3 will bump to a specific version and ignore other flags
*/
gulp.task('bump', function() {
var msg = 'Bumping versions';
var type = args.type;
var version = args.ver;
var options = {};
if (version) {
options.version = version;
msg += ' to ' + version;
} else {
options.type = type;
msg += ' for a ' + type;
}
log(msg);
return gulp
.src(config.packages)
.pipe($.print())
.pipe($.bump(options))
.pipe(gulp.dest(config.root));
});
////////////////
/**
* When files change, log it
* @param {Object} event - event that fired
*/
function changeEvent(event) {
var srcPattern = new RegExp('/.*(?=/' + config.source + ')/');
log('File ' + event.path.replace(srcPattern, '') + ' ' + event.type);
}
/**
* Delete all files in a given path
* @param {Array} path - array of paths to delete
* @param {Function} done - callback when complete
*/
function clean(path, done) {
log('Cleaning: ' + $.util.colors.blue(path));
del(path, done);
}
/**
* Inject files in a sorted sequence at a specified inject label
* @param {Array} src glob pattern for source files
* @param {String} label The label name
* @param {Array} order glob pattern for sort order of the files
* @returns {Stream} The stream
*/
function inject(src, label, order) {
var options = {read: false};
if (label) {
options.name = 'inject:' + label;
}
return $.inject(orderSrc(src, order), options);
}
/**
* Order a stream
* @param {Stream} src The gulp.src stream
* @param {Array} order Glob array pattern
* @returns {Stream} The ordered stream
*/
function orderSrc (src, order) {
//order = order || ['**/*'];
return gulp
.src(src)
.pipe($.if(order, $.order(order)));
}
/**
* serve the code
* --debug-brk or --debug
* --nosync
* @param {Boolean} isDev - dev or build mode
* @param {Boolean} specRunner - server spec runner html
*/
function serve(isDev, specRunner) {
var debug = args.debug || args.debugBrk;
var debugMode = args.debug ? '--debug' : args.debugBrk ? '--debug-brk' : '';
var nodeOptions = getNodeOptions(isDev);
if (debug) {
runNodeInspector();
nodeOptions.nodeArgs = [debugMode + '=5858'];
}
if (args.verbose) {
console.log(nodeOptions);
}
return $.nodemon(nodeOptions)
.on('restart', ['vet'], function(ev) {
log('*** nodemon restarted');
log('files changed:\n' + ev);
setTimeout(function() {
browserSync.notify('reloading now ...');
browserSync.reload({stream: false});
}, config.browserReloadDelay);
})
.on('start', function () {
log('*** nodemon started');
startBrowserSync(isDev, specRunner);
})
.on('crash', function () {
log('*** nodemon crashed: script crashed for some reason');
})
.on('exit', function () {
log('*** nodemon exited cleanly');
});
}
function getNodeOptions(isDev) {
return {
script: config.nodeServer,
delayTime: 1,
env: {
'PORT': port,
'NODE_ENV': isDev ? 'dev' : 'build'
},
watch: [config.server]
};
}
function runNodeInspector() {
log('Running node-inspector.');
log('Browse to http://localhost:8080/debug?port=5858');
var exec = require('child_process').exec;
exec('node-inspector');
}
/**
* Start BrowserSync
* --nosync will avoid browserSync
*/
function startBrowserSync(isDev, specRunner) {
if (args.nosync || browserSync.active) {
return;
}
log('Starting BrowserSync on port ' + port);
// If build: watches the files, builds, and restarts browser-sync.
// If dev: watches less, compiles it to css, browser-sync handles reload
if (isDev) {
gulp.watch([config.less], ['styles'])
.on('change', changeEvent);
} else {
gulp.watch([config.less, config.js, config.html], ['optimize', browserSync.reload])
.on('change', changeEvent);
}
var options = {
proxy: 'localhost:' + port,
port: 3000,
files: isDev ? [
config.client + '**/*.*',
'!' + config.less,
config.temp + '**/*.css'
] : [],
ghostMode: { // these are the defaults t,f,t,t
clicks: true,
location: false,
forms: true,
scroll: true
},
injectChanges: true,
logFileChanges: true,
logLevel: 'debug',
logPrefix: 'gulp-patterns',
notify: true,
reloadDelay: 0 //1000
} ;
if (specRunner) {
options.startPath = config.specRunnerFile;
}
browserSync(options);
}
/**
* Start Plato inspector and visualizer
*/
function startPlatoVisualizer(done) {
log('Running Plato');
var files = glob.sync(config.plato.js);
var excludeFiles = /.*\.spec\.js/;
var plato = require('plato');
var options = {
title: 'Plato Inspections Report',
exclude: excludeFiles
};
var outputDir = config.report + '/plato';
plato.inspect(files, outputDir, options, platoCompleted);
function platoCompleted(report) {
var overview = plato.getOverviewReport(report);
if (args.verbose) {
log(overview.summary);
}
if (done) { done(); }
}
}
/**
* Start the tests using karma.
* @param {boolean} singleRun - True means run once and end (CI), or keep running (dev)
* @param {Function} done - Callback to fire when karma is done
* @return {undefined}
*/
function startTests(singleRun, done) {
var child;
var excludeFiles = [];
var fork = require('child_process').fork;
var karma = require('karma').server;
var serverSpecs = config.serverIntegrationSpecs;
if (args.startServers) {
log('Starting servers');
var savedEnv = process.env;
savedEnv.NODE_ENV = 'dev';
savedEnv.PORT = 8888;
child = fork(config.nodeServer);
} else {
if (serverSpecs && serverSpecs.length) {
excludeFiles = serverSpecs;
}
}
karma.start({
configFile: __dirname + '/karma.conf.js',
exclude: excludeFiles,
singleRun: !!singleRun
}, karmaCompleted);
////////////////
function karmaCompleted(karmaResult) {
log('Karma completed');
if (child) {
log('shutting down the child process');
child.kill();
}
if (karmaResult === 1) {
done('karma: tests failed with code ' + karmaResult);
} else {
done();
}
}
}
/**
* Formatter for bytediff to display the size changes after processing
* @param {Object} data - byte data
* @return {String} Difference in bytes, formatted
*/
function bytediffFormatter(data) {
var difference = (data.savings > 0) ? ' smaller.' : ' larger.';
return data.fileName + ' went from ' +
(data.startSize / 1000).toFixed(2) + ' kB to ' +
(data.endSize / 1000).toFixed(2) + ' kB and is ' +
formatPercent(1 - data.percent, 2) + '%' + difference;
}
/**
* Log an error message and emit the end of a task
*/
function errorLogger(error) {
log('*** Start of Error ***');
log(error);
log('*** End of Error ***');
this.emit('end');
}
/**
* Format a number as a percentage
* @param {Number} num Number to format as a percent
* @param {Number} precision Precision of the decimal
* @return {String} Formatted perentage
*/
function formatPercent(num, precision) {
return (num * 100).toFixed(precision);
}
/**
* Format and return the header for files
* @return {String} Formatted file header
*/
function getHeader() {
var pkg = require('./package.json');
var template = ['/**',
' * <%%= pkg.name %> - <%%= pkg.description %>',
' * @authors <%%= pkg.authors %>',
' * @version v<%%= pkg.version %>',
' * @link <%%= pkg.homepage %>',
' * @license <%%= pkg.license %>',
' */',
''
].join('\n');
return $.header(template, {
pkg: pkg
});
}
/**
* Log a message or series of messages using chalk's blue color.
* Can pass in a string, object or array.
*/
function log(msg) {
if (typeof(msg) === 'object') {
for (var item in msg) {
if (msg.hasOwnProperty(item)) {
$.util.log($.util.colors.blue(msg[item]));
}
}
} else {
$.util.log($.util.colors.blue(msg));
}
}
/**
* Show OS level notification using node-notifier
*/
function notify(options) {
var notifier = require('node-notifier');
var notifyOptions = {
sound: 'Bottle',
contentImage: path.join(__dirname, 'gulp.png'),
icon: path.join(__dirname, 'gulp.png')
};
_.assign(notifyOptions, options);
notifier.notify(notifyOptions);
}
module.exports = gulp;