From 9ad4d74f40b91b891712ddf44eb35c278d1ecb61 Mon Sep 17 00:00:00 2001 From: Pawel Wszola Date: Wed, 30 Apr 2014 23:46:03 +0200 Subject: [PATCH] feat(gen): add Gulp support add an option to use Gulp instead of Grunt Closes #672 (+1 squashed commits) Squashed commits: [87a7e7c] feat(gen): add support for Gulp Add an option to use Gulp instead of Grunt --- app/index.js | 52 ++++++- templates/common/root/_Gulpfile.js | 220 ++++++++++++++++++++++++++++ templates/common/root/_package.json | 32 +++- 3 files changed, 293 insertions(+), 11 deletions(-) create mode 100644 templates/common/root/_Gulpfile.js diff --git a/app/index.js b/app/index.js index 84b77fd71..0805e41ce 100644 --- a/app/index.js +++ b/app/index.js @@ -150,15 +150,43 @@ Generator.prototype.welcome = function welcome() { } }; -Generator.prototype.askForCompass = function askForCompass() { +Generator.prototype.askForGulp = function askForGulp() { var cb = this.async(); this.prompt([{ + type: 'confirm', + name: 'gulp', + message: 'Would you like to use Gulp (experimental) instead of Grunt?', + default: false + }], function (props) { + this.gulp = props.gulp; + + cb(); + }.bind(this)); +}; + +Generator.prototype.askForStyles = function askForStyles() { + var gulp = this.gulp; + var cb = this.async(); + + this.prompt([{ + type: 'confirm', + name: 'sass', + message: 'Would you like to use Sass?', + default: true, + when: function () { + return gulp; + } + }, { type: 'confirm', name: 'compass', message: 'Would you like to use Sass (with Compass)?', - default: true + default: true, + when: function () { + return !gulp; + } }], function (props) { + this.sass = props.sass; this.compass = props.compass; cb(); @@ -167,6 +195,7 @@ Generator.prototype.askForCompass = function askForCompass() { Generator.prototype.askForBootstrap = function askForBootstrap() { var compass = this.compass; + var gulp = this.gulp; var cb = this.async(); this.prompt([{ @@ -180,7 +209,7 @@ Generator.prototype.askForBootstrap = function askForBootstrap() { message: 'Would you like to use the Sass version of Bootstrap?', default: true, when: function (props) { - return props.bootstrap && compass; + return !gulp && (props.bootstrap && compass); } }], function (props) { this.bootstrap = props.bootstrap; @@ -294,8 +323,9 @@ Generator.prototype.readIndex = function readIndex() { }; Generator.prototype.bootstrapFiles = function bootstrapFiles() { - var cssFile = 'styles/main.' + (this.compass ? 's' : '') + 'css'; - this.copy( + var sass = this.compass || this.sass; + var cssFile = 'styles/main.' + (sass ? 's' : '') + 'css'; + this.copy( path.join('app', cssFile), path.join(this.appPath, cssFile) ); @@ -322,22 +352,28 @@ Generator.prototype.packageFiles = function packageFiles() { this.template('root/_bower.json', 'bower.json'); this.template('root/_bowerrc', '.bowerrc'); this.template('root/_package.json', 'package.json'); - this.template('root/_Gruntfile.js', 'Gruntfile.js'); + if (this.gulp) { + this.template('root/_Gulpfile.js', 'Gulpfile.js'); + } else { + this.template('root/_Gruntfile.js', 'Gruntfile.js'); + } if (this.typescript) { this.template('root/_tsd.json', 'tsd.json'); } this.template('root/README.md', 'README.md'); + }; Generator.prototype._injectDependencies = function _injectDependencies() { + var taskRunner = this.gulp ? 'gulp' : 'grunt'; if (this.options['skip-install']) { this.log( 'After running `npm install & bower install`, inject your front end dependencies' + '\ninto your source code by running:' + '\n' + - '\n' + chalk.yellow.bold('grunt wiredep') + '\n' + chalk.yellow.bold(taskRunner + ' wiredep') ); } else { - this.spawnCommand('grunt', ['wiredep']); + this.spawnCommand(taskRunner, ['wiredep']); } }; diff --git a/templates/common/root/_Gulpfile.js b/templates/common/root/_Gulpfile.js new file mode 100644 index 000000000..71858fc55 --- /dev/null +++ b/templates/common/root/_Gulpfile.js @@ -0,0 +1,220 @@ +// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %> +'use strict'; + +var gulp = require('gulp'); +var $ = require('gulp-load-plugins')(); +var openURL = require('open'); +var lazypipe = require('lazypipe'); +var rimraf = require('rimraf'); +var wiredep = require('wiredep').stream; +var runSequence = require('run-sequence'); + +var yeoman = { + app: require('./bower.json').appPath || 'app', + dist: 'dist' +}; + +var paths = { + scripts: [yeoman.app + '/scripts/**/*.<% if (coffee) { %>coffee<% } else { %>js<% } %>'], + styles: [yeoman.app + '/styles/**/*.<% if (sass) { %>scss<% } else { %>css<% } %>'], + test: ['test/spec/**/*.<% if (coffee) { %>coffee<% } else { %>js<% } %>'], + testRequire: [ + yeoman.app + '/bower_components/angular/angular.js', + yeoman.app + '/bower_components/angular-mocks/angular-mocks.js', + yeoman.app + '/bower_components/angular-resource/angular-resource.js', + yeoman.app + '/bower_components/angular-cookies/angular-cookies.js', + yeoman.app + '/bower_components/angular-sanitize/angular-sanitize.js', + yeoman.app + '/bower_components/angular-route/angular-route.js',<% if (coffee) { %> + 'test/mock/**/*.coffee', + 'test/spec/**/*.coffee'<% } else { %> + 'test/mock/**/*.js', + 'test/spec/**/*.js'<% } %> + ], + karma: 'karma.conf.js', + views: { + main: yeoman.app + '/index.html', + files: [yeoman.app + '/views/**/*.html'] + } +}; + +//////////////////////// +// Reusable pipelines // +//////////////////////// + +var lintScripts = lazypipe()<% if (coffee) { %> + .pipe($.coffeelint) + .pipe($.coffeelint.reporter);<% } else { %> + .pipe($.jshint, '.jshintrc') + .pipe($.jshint.reporter, 'jshint-stylish');<% } %> + +var styles = lazypipe()<% if (sass) { %> + .pipe($.rubySass, { + style: 'expanded', + precision: 10 + })<% } %> + .pipe($.autoprefixer, 'last 1 version') + .pipe(gulp.dest, '.tmp/styles'); + +/////////// +// Tasks // +/////////// + +gulp.task('styles', function () { + return gulp.src(paths.styles) + .pipe(styles()); +});<% if (coffee) { %> + +gulp.task('coffee', function() { + return gulp.src(paths.scripts) + .pipe(lintScripts()) + .pipe($.coffee({bare: true}).on('error', $.util.log)) + .pipe(gulp.dest('.tmp/scripts')); +});<% } %> + +gulp.task('lint:scripts', function () { + return gulp.src(paths.scripts) + .pipe(lintScripts()); +}); + +gulp.task('clean:tmp', function (cb) { + rimraf('./.tmp', cb); +}); + +gulp.task('start:client', ['start:server', <% if (coffee) { %>'coffee', <% } %>'styles'], function () { + openURL('http://localhost:9000'); +}); + +gulp.task('start:server', function() { + $.connect.server({ + root: [yeoman.app, '.tmp'], + livereload: true, + // Change this to '0.0.0.0' to access the server from outside. + port: 9000 + }); +}); + +gulp.task('start:server:test', function() { + $.connect.server({ + root: ['test', yeoman.app, '.tmp'], + livereload: true, + port: 9001 + }); +}); + +gulp.task('watch', function () { + + $.watch({glob: paths.styles}) + .pipe($.plumber()) + .pipe(styles()) + .pipe($.connect.reload()); + + $.watch({glob: paths.views.files}) + .pipe($.plumber()) + .pipe($.connect.reload()); + + $.watch({glob: paths.scripts}) + .pipe($.plumber()) + .pipe(lintScripts())<% if (coffee) { %> + .pipe($.coffee({bare: true}).on('error', $.util.log)) + .pipe(gulp.dest('.tmp/scripts'))<% } %> + .pipe($.connect.reload()); + + $.watch({glob: paths.test}) + .pipe($.plumber()) + .pipe(lintScripts()); + + gulp.watch('bower.json', ['bower']); +}); + +gulp.task('serve', function (callback) { + runSequence('clean:tmp', + ['lint:scripts'], + ['start:client'], + 'watch', callback); +}); + +gulp.task('serve:prod', function() { + $.connect.server({ + root: [yeoman.dist], + livereload: true, + port: 9000 + }); +}); + +gulp.task('test', ['start:server:test'], function () { + var testToFiles = paths.testRequire.concat(paths.scripts, paths.test); + return gulp.src(testToFiles) + .pipe($.karma({ + configFile: paths.karma, + action: 'watch' + })); +}); + +// inject bower components +gulp.task('bower', function () { + return gulp.src(paths.views.main) + .pipe(wiredep({ + directory: yeoman.app + '/bower_components', + ignorePath: '..' + })) + .pipe(gulp.dest(yeoman.app + '/views')); +}); + +/////////// +// Build // +/////////// + +gulp.task('build', function (callback) { + runSequence('clean:dist', + ['images', 'copy:extras', 'copy:fonts', 'client:build'], + callback); +}); + +gulp.task('clean:dist', function () { + rimraf('./dist', cb); +}); + +gulp.task('client:build', ['html', 'styles'], function () { + var jsFilter = $.filter('**/*.js'); + var cssFilter = $.filter('**/*.css'); + + return gulp.src(paths.views.main) + .pipe($.useref.assets({searchPath: [yeoman.app, '.tmp']})) + .pipe(jsFilter) + .pipe($.ngAnnotate()) + .pipe($.uglify()) + .pipe(jsFilter.restore()) + .pipe(cssFilter) + .pipe($.minifyCss({cache: true})) + .pipe(cssFilter.restore()) + .pipe($.rev()) + .pipe($.useref.restore()) + .pipe($.revReplace()) + .pipe($.useref()) + .pipe(gulp.dest(yeoman.dist)); +}); + +gulp.task('html', function () { + return gulp.src(yeoman.app + '/views/**/*') + .pipe(gulp.dest(yeoman.dist + '/views')); +}); + +gulp.task('images', function () { + return gulp.src(yeoman.app + '/images/**/*') + .pipe($.cache($.imagemin({ + optimizationLevel: 5, + progressive: true, + interlaced: true + }))) + .pipe(gulp.dest(yeoman.dist + '/images')); +}); + +gulp.task('copy:extras', function () { + return gulp.src(yeoman.app + '/*/.*', { dot: true }) + .pipe(gulp.dest(yeoman.dist)); +}); + +gulp.task('copy:fonts', function () { + return gulp.src(yeoman.app + '/fonts/**/*') + .pipe(gulp.dest(yeoman.dist + '/fonts')); +}); diff --git a/templates/common/root/_package.json b/templates/common/root/_package.json index 2dd638508..fd016a2d1 100644 --- a/templates/common/root/_package.json +++ b/templates/common/root/_package.json @@ -1,7 +1,33 @@ { "name": "<%= _.slugify(appname) %>", "private": true, - "devDependencies": { + "devDependencies": {<% if (gulp) { %> + "gulp": "^3.9.0", + "gulp-connect": "^2.2.0", + "gulp-autoprefixer": "2.3.1", + "gulp-cache": "^0.2.10", + "rimraf": "^2.4.0", + "gulp-filter": "^2.0.2", + "gulp-imagemin": "^2.3.0", + "gulp-jshint": "^1.11.1", + "gulp-karma": "0.0.4", + "gulp-load-plugins": "^0.10.0", + "gulp-plumber": "^1.0.1", + "gulp-minify-css": "^1.2.0", + "gulp-rev": "^5.0.1", + "gulp-rev-replace": "^0.4.2", + "gulp-uglify": "^1.2.0", + "gulp-useref": "^1.2.0", + "gulp-util": "^3.0.6", + "gulp-watch": "^4.2.4", + "run-sequence": "^1.1.1", + "wiredep": "^2.2.2", + "lazypipe": "^0.2.4", + "gulp-ng-annotate": "^1.0.0", + "open": "0.0.5"<% if (sass) { %>, + "gulp-ruby-sass": "^0.4.3"<% } %><% if (coffee) { %>, + "gulp-coffeelint": "^0.5.0", + "gulp-coffee": "^2.3.1",<% } %><% } else { %> "autoprefixer-core": "^5.2.1", "grunt": "^0.4.5", "grunt-angular-templates": "^0.5.7", @@ -30,8 +56,8 @@ "grunt-usemin": "^3.0.0", "grunt-wiredep": "^2.0.0", "jit-grunt": "^0.9.1", - "jshint-stylish": "^1.0.0", - "time-grunt": "^1.0.0" + "time-grunt": "^1.0.0",<% } %> + "jshint-stylish": "^1.0.0" }, "engines": { "node": ">=0.10.0"