From c70fd472150a0ea1f4fda3a6909da7cb3c6a3dc5 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 19 Aug 2015 11:29:23 -0400 Subject: [PATCH] feat(gulp): add experimental Gulp support to enable, run `yo angular-fullstack` with the `--gulp` flag --- app/generator.js | 28 +- .../{Gruntfile.js => Gruntfile(grunt).js} | 0 app/templates/_package.json | 87 +++- app/templates/gulpfile.babel(gulp).js | 478 ++++++++++++++++++ readme.md | 3 +- test/test-file-creation.js | 1 + 6 files changed, 570 insertions(+), 27 deletions(-) rename app/templates/{Gruntfile.js => Gruntfile(grunt).js} (100%) create mode 100644 app/templates/gulpfile.babel(gulp).js diff --git a/app/generator.js b/app/generator.js index 64db0c1a5..40e8e7b50 100644 --- a/app/generator.js +++ b/app/generator.js @@ -19,6 +19,12 @@ export default class Generator extends Base { defaults: false }); + this.option('gulp', { + desc: 'Use experimental Gulp configuration', + type: Boolean, + defaults: false + }); + this.option('app-suffix', { desc: 'Allow a custom suffix to be added to the module name', type: String, @@ -129,6 +135,11 @@ export default class Generator extends Base { this.filters[answers.router] = true; this.filters.bootstrap = !!answers.bootstrap; this.filters.uibootstrap = !!answers.uibootstrap; + + this.scriptExt = answers.script === 'coffee' ? 'coffee' : 'js'; + this.templateExt = answers.markup; + this.styleExt = answers.stylesheet; + cb(); }.bind(this)); }, @@ -243,7 +254,14 @@ export default class Generator extends Base { this.log('\n# Project\n'); - this.prompt([{ + this.prompt([/*{ + type: 'list', + name: 'buildtool', + message: 'Would you like to use Gulp (experimental) instead of Grunt?', + choices: ['Grunt', 'Gulp'], + default: 0, + filter: val => val.toLowerCase() + }, */{ type: 'list', name: 'testing', message: 'What would you like to write tests with?', @@ -268,10 +286,10 @@ export default class Generator extends Base { return answers.testing === 'mocha'; } }], function (answers) { - /** - * Default to grunt until gulp support is implemented - */ - this.filters.grunt = true; + this.filters.grunt = !this.options['gulp']; + this.filters.gulp = !!this.options['gulp']; + // this.filters.grunt = answers.buildtool === 'grunt'; + // this.filters.gulp = answers.buildtool === 'gulp'; this.filters[answers.testing] = true; if (answers.testing === 'mocha') { diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile(grunt).js similarity index 100% rename from app/templates/Gruntfile.js rename to app/templates/Gruntfile(grunt).js diff --git a/app/templates/_package.json b/app/templates/_package.json index f070f4216..09d2e0ea6 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -37,7 +37,52 @@ }, "devDependencies": { "autoprefixer": "^6.0.0", - "babel-core": "^5.6.4", + "babel-core": "^5.6.4",<% if(filters.gulp) { %> + "del": "^2.0.2", + "gulp": "^3.9.0", + "gulp-add-src": "^0.2.0", + "gulp-angular-templatecache": "^1.7.0", + "gulp-autoprefixer": "2.3.1",<% if(filters.babel) { %> + "gulp-babel": "^5.1.0",<% } %> + "gulp-cache": "^0.2.10", + "gulp-concat": "^2.6.0", + "gulp-filter": "^2.0.2", + "gulp-imagemin": "^2.2.1", + "gulp-inject": "^1.3.1", + "gulp-jshint": "^1.11.0",<% if(filters.less) { %> + "gulp-less": "3.0.3",<% } %> + "gulp-livereload": "^3.8.0", + "gulp-load-plugins": "^1.0.0-rc.1", + "gulp-minify-css": "^1.1.6", + "gulp-mocha": "^2.1.3", + "gulp-ng-annotate": "^1.1.0", + "gulp-ng-constant": "^1.1.0", + "gulp-plumber": "^1.0.1", + "gulp-rename": "^1.2.2", + "gulp-rev": "^5.0.0", + "gulp-rev-replace": "^0.4.2", + "gulp-sort": "^1.1.1", + "gulp-sourcemaps": "^1.5.2", + "gulp-svgmin": "^1.1.2", + "gulp-uglify": "^1.2.0", + "gulp-useref": "^1.2.0", + "gulp-util": "^3.0.5", + "gulp-watch": "^4.3.5",<% if(filters.jade) { %> + "gulp-jade": "^1.0.1",<% } if(filters.stylus) { %> + "gulp-stylus": "^2.0.4", + "gulp-stylint": "^1.1.3", + "nib": "^1.1.0",<% } if(filters.sass) { %> + "gulp-sass": "^2.0.1", + "gulp-scss-lint": "^0.2.1",<% } if(filters.less) { %> + "gulp-less": "^3.0.3", + "gulp-recess": "^1.1.2",<% } if(filters.coffee) { %> + "gulp-coffeelint": "^0.5.0", + "gulp-coffee": "^2.3.1",<% } %> + "utile": "~0.3.0", + "nodemon": "^1.3.7", + "run-sequence": "^1.1.0", + "lazypipe": "^0.2.4", + "wiredep": "^2.2.2",<% } /*end gulp*/ if(filters.grunt) { %> "grunt": "~0.4.5", "grunt-wiredep": "^2.0.0", "grunt-concurrent": "^2.0.1", @@ -49,11 +94,9 @@ "grunt-contrib-jshint": "~0.11.2", "grunt-contrib-uglify": "~0.11.0", "grunt-contrib-watch": "~0.6.1",<% if (filters.jade) { %> - "grunt-contrib-jade": "^0.15.0", - "karma-ng-jade2js-preprocessor": "^0.2.0",<% } %><% if (filters.less) { %> + "grunt-contrib-jade": "^0.15.0",<% } %><% if (filters.less) { %> "grunt-contrib-less": "^1.0.0",<% } %><% if(filters.babel) { %> - "karma-babel-preprocessor": "^5.2.1",<% } %> - "grunt-babel": "~5.0.0", + "grunt-babel": "~5.0.0",<% } %> "grunt-google-cdn": "~0.4.0", "grunt-jscs": "^2.1.0", "grunt-newer": "^1.1.1", @@ -73,39 +116,41 @@ "grunt-contrib-sass": "^0.9.0",<% } %><% if(filters.stylus) { %> "grunt-contrib-stylus": "~0.22.0",<% } %> "jit-grunt": "^0.9.1", - "time-grunt": "^1.2.1", "grunt-express-server": "^0.5.1", "grunt-postcss": "~0.7.1", "grunt-open": "~0.2.3", + "time-grunt": "^1.2.1", + "grunt-mocha-test": "~0.12.7", + "grunt-mocha-istanbul": "^3.0.1",<% } /*end grunt*/ %> "open": "~0.0.4", "jshint-stylish": "~2.1.0", "connect-livereload": "^0.5.3", - "mocha": "^2.2.5", - "grunt-mocha-test": "~0.12.7", - "grunt-mocha-istanbul": "^3.0.1", "istanbul": "~0.4.1", "chai": "^3.2.0", "sinon": "^1.16.1", "chai-as-promised": "^5.1.0", "chai-things": "^0.2.0", - "sinon-chai": "^2.8.0",<% if (filters.mocha) { %> - "karma-mocha": "^0.2.0", - "karma-chai-plugins": "^0.6.0",<% } if (filters.jasmine) { %> - "jasmine-core": "^2.3.4", - "karma-jasmine": "~0.3.0", - "jasmine-spec-reporter": "^2.4.0",<% } %> + "karma": "~0.13.3", "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.6", "karma-script-launcher": "~0.1.0", "karma-chrome-launcher": "~0.2.0", - "requirejs": "~2.1.11", "karma-requirejs": "~0.2.2", "karma-jade-preprocessor": "0.0.11", - "phantomjs": "^1.9.18", - "karma-phantomjs-launcher": "~0.2.0", - "karma": "~0.13.3", - "karma-ng-html2js-preprocessor": "~0.2.0", + "karma-phantomjs-launcher": "~0.2.0",<% if (filters.jade) { %> + "karma-ng-jade2js-preprocessor": "^0.2.0",<% } else { %> + "karma-ng-html2js-preprocessor": "~0.2.0",<% } %> "karma-spec-reporter": "~0.0.20", + "sinon-chai": "^2.8.0",<% if (filters.mocha) { %> + "mocha": "^2.2.5", + "karma-mocha": "^0.2.0", + "karma-chai-plugins": "^0.6.0",<% } if (filters.jasmine) { %> + "jasmine-core": "^2.3.4", + "karma-jasmine": "~0.3.0", + "jasmine-spec-reporter": "^2.4.0",<% } if(filters.babel) { %> + "karma-babel-preprocessor": "^5.2.1",<% } %> + "requirejs": "~2.1.11", + "phantomjs": "^1.9.18", "proxyquire": "^1.0.1", "supertest": "^1.1.0" }, @@ -115,7 +160,7 @@ }, "scripts": { "start": "node server", - "test": "grunt test", + "test": "<%= filters.grunt ? 'grunt' : 'gulp' %> test", "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" }, "private": true diff --git a/app/templates/gulpfile.babel(gulp).js b/app/templates/gulpfile.babel(gulp).js new file mode 100644 index 000000000..18f8d1d0e --- /dev/null +++ b/app/templates/gulpfile.babel(gulp).js @@ -0,0 +1,478 @@ +// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= rootGeneratorName() %> <%= rootGeneratorVersion() %> +'use strict'; + +import _ from 'lodash'; +import del from 'del'; +import gulp from 'gulp'; +import path from 'path'; +import gulpLoadPlugins from 'gulp-load-plugins'; +import http from 'http'; +import open from 'open'; +import lazypipe from 'lazypipe'; +import {stream as wiredep} from 'wiredep'; +import nodemon from 'nodemon'; +import runSequence from 'run-sequence';<% if(filters.stylus) { %> +import nib from 'nib';<% } %> + +var plugins = gulpLoadPlugins(); +var config; + +const paths = { + appPath: require('./bower.json').appPath || 'client', + client: { + assets: 'client/assets/**/*', + images: 'client/assets/images/*', + scripts: [ + 'client/**/*.<%= scriptExt %>', + '!client/bower_components/**/*.js' + ], + styles: ['client/{app,components}/**/*.<%= styleExt %>'], + mainStyle: 'client/app/app.<%= styleExt %>', + views: 'client/{app,components}/**/*.<%= templateExt %>', + mainView: 'client/index.html', + test: ['client/**/*.spec.<%= scriptExt %>'], + testRequire: [ + 'client/bower_components/angular/angular.js', + 'client/bower_components/angular-mocks/angular-mocks.js', + 'client/bower_components/angular-resource/angular-resource.js', + 'client/bower_components/angular-cookies/angular-cookies.js', + 'client/bower_components/angular-sanitize/angular-sanitize.js', + 'client/bower_components/angular-route/angular-route.js', + 'client/**/*.spec.<%= scriptExt %>' + ], + bower: 'client/bower_components/' + }, + server: { + scripts: ['server/**/*.<%= scriptExt %>'], + json: ['server/**/*.json'], + test: [ + 'server/**/*.spec.js', + 'server/**/*.mock.js', + 'server/**/*.integration.js' + ] + }, + karma: 'karma.conf.js', + dist: 'dist' +}; + +/******************** + * Helper functions + ********************/ + +function onServerLog(log) { + console.log(plugins.util.colors.white('[') + + plugins.util.colors.yellow('nodemon') + + plugins.util.colors.white('] ') + + log.message); +} + +function checkAppReady(cb) { + var options = { + host: 'localhost', + port: config.port + }; + http + .get(options, () => cb(true)) + .on('error', () => cb(false)); +} + +// Call page until first success +function whenServerReady(cb) { + var serverReady = false; + var appReadyInterval = setInterval(() => + checkAppReady((ready) => { + if (!ready || serverReady) { + return; + } + clearInterval(appReadyInterval); + serverReady = true; + cb(); + }), + 100); +} + +/******************** + * Reusable pipelines + ********************/ + +let lintClientScripts = lazypipe()<% if(filters.coffee) { %> + .pipe(plugins.coffeelint) + .pipe(plugins.coffeelint.reporter);<% } else { %> + .pipe(plugins.jshint, 'client/.jshintrc') + .pipe(plugins.jshint.reporter, 'jshint-stylish');<% } %> + +let lintServerScripts = lazypipe()<% if(filters.coffee) { %> + .pipe(plugins.coffeelint) + .pipe(plugins.coffeelint.reporter);<% } else { %> + .pipe(plugins.jshint, 'server/.jshintrc') + .pipe(plugins.jshint.reporter, 'jshint-stylish');<% } %> + +let styles = lazypipe() + .pipe(plugins.sourcemaps.init)<% if(filters.stylus) { %> + .pipe(plugins.stylus, { + use: [nib()], + errors: true + })<% } if(filters.sass) { %> + .pipe(plugins.sass)<% } if(filters.less) { %> + .pipe(plugins.less)<% } %> + .pipe(plugins.autoprefixer, {browsers: ['last 1 version']}) + .pipe(plugins.sourcemaps.write, '.');<% if(filters.babel || filters.coffee) { %> + +let transpile = lazypipe() + .pipe(plugins.sourcemaps.init)<% if(filters.babel) { %> + .pipe(plugins.babel)<% } else { %> + .pipe(plugins.coffee, {bare: true})<% } %> + .pipe(plugins.sourcemaps.write, '.');<% } %> + +/******************** + * Env + ********************/ + +gulp.task('env:all', () => { + let localConfig; + try { + localConfig = require('./server/config/local.env'); + } catch (e) { + localConfig = {}; + } + plugins.env({ + vars: localConfig + }); +}); +gulp.task('env:test', () => { + plugins.env({ + vars: {NODE_ENV: 'test'} + }); +}); +gulp.task('env:prod', () => { + plugins.env({ + vars: {NODE_ENV: 'production'} + }); +}); + +/******************** + * Tasks + ********************/ + +gulp.task('inject', cb => { + runSequence(['inject:js', 'inject:css', 'inject:<%= styleExt %>'], cb); +}); + +gulp.task('inject:js', () => { + return gulp.src(paths.client.mainView) + .pipe(plugins.inject( + gulp.src(_.union(paths.client.scripts, ['!client/**/*.spec.<%= scriptExt %>']), {read: false}) + .pipe(plugins.sort()), + { + starttag: '', + endtag: '', + transform: (filepath) => '' + })) + .pipe(gulp.dest('client')); +}); + +gulp.task('inject:css', () => { + return gulp.src(paths.client.mainView) + .pipe(plugins.inject( + gulp.src('/client/**/*.css', {read: false}) + .pipe(plugins.sort()), + { + starttag: '', + endtag: '', + transform: (filepath) => '' + })) + .pipe(gulp.dest('client')); +}); + +gulp.task('inject:<%= styleExt %>', () => { + return gulp.src('client/app/app.<%= styleExt %>') + .pipe(plugins.inject( + gulp.src(_.union(paths.client.styles, ['!' + paths.client.mainStyle]), {read: false}) + .pipe(plugins.sort()), + { + starttag: '// injector', + endtag: '// endinjector', + transform: (filepath) => { + let newPath = filepath + .replace('/client/app/', '') + .replace('/client/components/', '../components/') + .replace(/_(.*).<%= styleExt %>/, (match, p1, offset, string) => p1) + .replace('.<%= styleExt %>', ''); + return '@import \'' + newPath + '\';'; + } + })) + .pipe(gulp.dest('client/app')); +}); + +gulp.task('styles', () => { + return gulp.src(paths.client.mainStyle) + .pipe(styles()) + .pipe(gulp.dest('.tmp/app')); +});<% if(filters.babel || filters.coffee) { %> + +gulp.task('transpile:client', () => { + return gulp.src(paths.client.scripts) + .pipe(transpile()) + .pipe(gulp.dest('.tmp')); +});<% } %> + +gulp.task('transpile:server', () => { + return gulp.src(_.union(paths.server.scripts, paths.server.json)) + .pipe(transpile()) + .pipe(gulp.dest(paths.dist + '/server')); +}); + +gulp.task('lint:scripts', cb => runSequence(['lint:scripts:client', 'lint:scripts:server'], cb)); + +gulp.task('lint:scripts:client', () => { + return gulp.src(_.union(paths.client.scripts, _.map(paths.client.test, blob => '!' + blob))) + .pipe(lintClientScripts()); +}); + +gulp.task('lint:scripts:server', () => { + return gulp.src(_.union(paths.server.scripts, _.map(paths.server.test, blob => '!' + blob))) + .pipe(lintServerScripts()); +}); + +gulp.task('clean:tmp', () => del(['.tmp/**/*'])); + +gulp.task('start:client', cb => { + whenServerReady(() => { + open('http://localhost:' + config.port); + cb(); + }); +}); + +gulp.task('start:server', () => { + process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + config = require('./server/config/environment'); + nodemon('-w server server') + .on('log', onServerLog); +}); + +gulp.task('watch', () => { + var testFiles = _.union(paths.client.test, paths.server.test); + + plugins.livereload.listen(); + + plugins.watch(paths.client.styles, () => { //['inject:<%= styleExt %>'] + gulp.src(paths.client.mainStyle) + .pipe(plugins.plumber()) + .pipe(styles()) + .pipe(gulp.dest('.tmp/app')) + .pipe(plugins.livereload()); + }); + + plugins.watch(paths.client.views) + .pipe(plugins.plumber()) + .pipe(plugins.livereload()); + + plugins.watch(paths.client.scripts) //['inject:js'] + .pipe(plugins.plumber())<% if(filters.babel || filters.coffee) { %> + .pipe(transpile()) + .pipe(gulp.dest('.tmp'))<% } %> + .pipe(plugins.livereload()); + + plugins.watch(_.union(paths.server.scripts, testFiles)) + .pipe(plugins.plumber()) + .pipe(lintServerScripts()) + .pipe(plugins.livereload()); + + gulp.watch('bower.json', ['wiredep:client']); +}); + +gulp.task('serve', cb => { + runSequence(['clean:tmp', 'constant'], + ['lint:scripts', 'inject'<% if(filters.jade) { %>, 'jade'<% } %>], + ['wiredep:client'],<% if(filters.babel || filters.coffee) { %> + ['transpile:client', 'styles'],<% } else { %> + 'styles',<% } %> + ['start:server', 'start:client'], + 'watch', + cb); +}); + +gulp.task('test', cb => { + return runSequence('test:server', 'test:client', cb); +}); + +gulp.task('test:server', cb => { + runSequence( + 'env:all', + 'env:test', + 'mocha:unit', + //'mocha:coverage', + cb); +}); + +gulp.task('mocha:unit', () => { + return gulp.src(paths.server.test) + .pipe(plugins.mocha({ + reporter: 'spec', + require: [ + './mocha.conf' + ] + })) + .once('end', function() { + process.exit(); + }); +}); + +gulp.task('test:client', () => { + let testFiles = _.union(paths.client.testRequire, paths.client.test); + return gulp.src(testFiles) + .pipe(plugins.karma({ + configFile: paths.karma, + action: 'watch' + })); +}); + +// inject bower components +gulp.task('wiredep:client', () => { + return gulp.src(paths.client.mainView) + .pipe(wiredep({ + exclude: [ + /bootstrap-sass-official/, + /bootstrap.js/, + /json3/, + /es5-shim/, + /bootstrap.css/, + /font-awesome.css/ + ], + ignorePath: paths.appPath + })) + .pipe(gulp.dest('client/')); +}); + +gulp.task('wiredep:test', () => { + gulp.src(paths.karma) + .pipe(wiredep({ + exclude: [ + /bootstrap-sass-official/, + /bootstrap.js/, + '/json3/', + '/es5-shim/', + /bootstrap.css/, + /font-awesome.css/ + ], + devDependencies: true + })) + .pipe(gulp.dest('./')); +}); + +/******************** + * Build + ********************/ + +//FIXME: looks like font-awesome isn't getting loaded +gulp.task('build', cb => { + runSequence( + 'clean:dist', + 'inject', + 'wiredep:client', + [ + 'build:images', + 'copy:extras', + 'copy:assets', + 'copy:server', + 'transpile:server', + 'build:client' + ], + cb); +}); + +gulp.task('clean:dist', () => del(['dist/**/*'])); + +gulp.task('build:client', ['transpile:client', 'styles', 'html'], () => { + var appFilter = plugins.filter('**/app.js'); + var jsFilter = plugins.filter('**/*.js'); + var cssFilter = plugins.filter('**/*.css'); + var htmlFilter = plugins.filter('**/*.html');<% if(filters.jade) { %> + var assetsFilter = plugins.filter('**/*.{js,css}');<% } %> + + let assets = plugins.useref.assets({searchPath: ['client', '.tmp']}); + + return gulp.src(paths.mainView)<% if(filters.jade) { %> + .pipe(plugins.jade({pretty: true}))<% } %> + .pipe(assets) + .pipe(appFilter) + .pipe(plugins.addSrc.append('.tmp/templates.js')) + .pipe(plugins.concat('app/app.js')) + .pipe(appFilter.restore()) + .pipe(jsFilter) + .pipe(plugins.ngAnnotate()) + .pipe(plugins.uglify()) + .pipe(jsFilter.restore()) + .pipe(cssFilter) + .pipe(plugins.minifyCss({ + cache: true, + processImportFrom: ['!fonts.googleapis.com'] + })) + .pipe(cssFilter.restore()) + .pipe(plugins.rev()) + .pipe(assets.restore()) + .pipe(plugins.revReplace()) + .pipe(plugins.useref())<% if(filters.jade) { %> + .pipe(assetsFilter)<% } %> + .pipe(gulp.dest(paths.dist + '/client')); +}); + +gulp.task('html', function() { + return gulp.src('client/{app,components}/**/*.html') + .pipe(plugins.angularTemplatecache({ + module: '<%= scriptAppName %>' + })) + .pipe(gulp.dest('.tmp')); +});<% if (filters.jade) { %> +gulp.task('jade', function() { + gulp.src(paths.client.views) + .pipe(plugins.jade()) + .pipe(gulp.dest('.tmp')); +});<% } %> + +gulp.task('constant', function() { + let sharedConfig = require('./server/config/environment/shared'); + plugins.ngConstant({ + name: '<%= scriptAppName %>.constants', + deps: [], + wrap: true, + stream: true, + constants: { appConfig: sharedConfig } + }) + .pipe(plugins.rename({ + basename: 'app.constant' + })) + .pipe(gulp.dest('client/app/')) +}) + +gulp.task('build:images', () => { + return gulp.src('client/assets/images/**/*') + .pipe(plugins.imagemin({ + optimizationLevel: 5, + progressive: true, + interlaced: true + })) + .pipe(gulp.dest(paths.dist + '/client/assets/images')); +}); + +gulp.task('copy:extras', () => { + return gulp.src([ + 'client/favicon.ico', + 'client/robots.txt' + ], { dot: true }) + .pipe(gulp.dest(paths.dist + '/client')); +}); + +gulp.task('copy:assets', () => { + return gulp.src([paths.client.assets, '!' + paths.client.images]) + .pipe(gulp.dest(paths.dist + '/client/assets')); +}); + +gulp.task('copy:server', () => { + return gulp.src([ + 'package.json', + 'bower.json', + '.bowerrc' + ], {cwdbase: true}) + .pipe(gulp.dest(paths.dist)); +}); diff --git a/readme.md b/readme.md index 213fd4ca1..1a88fe332 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ Run `grunt` for building, `grunt serve` for preview, and `grunt serve:dist` for **General** -* Build Systems: `Grunt`, `Gulp` (Coming Soon) +* Build Systems: `Grunt`, `Gulp` (experimental) * Testing: * `Jasmine` * `Mocha + Chai + Sinon` @@ -113,6 +113,7 @@ Options: --skip-cache # Do not remember prompt answers Default: false --skip-install # Do not install dependencies Default: false --app-suffix # Allow a custom suffix to be added to the module name Default: App + --gulp # Use the experimental Gulp config instead of Grunt Default: false Arguments: name Type: String Required: false diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 555036c94..b7bbe9b70 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -11,6 +11,7 @@ var recursiveReadDir = require('recursive-readdir'); describe('angular-fullstack generator', function () { var gen, defaultOptions = { + buildtool: 'grunt', script: 'js', babel: true, markup: 'html',