-
+
<% if (filters.auth) { %>
<% } %>
diff --git a/templates/app/client/components/navbar/navbar(jade).jade b/templates/app/client/components/navbar/navbar(jade).jade
index d8a86b647..76a4ed76c 100644
--- a/templates/app/client/components/navbar/navbar(jade).jade
+++ b/templates/app/client/components/navbar/navbar(jade).jade
@@ -1,34 +1,34 @@
-div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarController')
+div.navbar.navbar-default.navbar-static-top
div.container
div.navbar-header
- button.navbar-toggle(type='button', ng-click='nav.isCollapsed = !nav.isCollapsed')
+ button.navbar-toggle(type='button', ng-click='$ctrl.isCollapsed = !$ctrl.isCollapsed')
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand(href='/') <%= lodash.slugify(lodash.humanize(appname)) %>
- div#navbar-main.navbar-collapse.collapse(uib-collapse='nav.isCollapsed')
- ul.nav.navbar-nav
- li(ng-repeat='item in nav.menu', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: nav.isActive(item.link)}'<% } %>)
+ div#navbar-main.navbar-collapse.collapse(uib-collapse='$ctrl.isCollapsed')
+ ul.$ctrl.navbar-nav
+ li(ng-repeat='item in $ctrl.menu', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: $ctrl.isActive(item.link)}'<% } %>)
a(<% if (filters.uirouter) { %>ui-sref='{{item.state}}'<% } else { %>ng-href='{{item.link}}'<% } %>) {{item.title}}<% if (filters.auth) { %>
- li(ng-show='nav.isAdmin()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: nav.isActive("/admin")}'<% } %>)
+ li(ng-show='$ctrl.isAdmin()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: $ctrl.isActive("/admin")}'<% } %>)
a(<% if (filters.uirouter) { %>ui-sref='admin'<% } else { %>href='/admin'<% } %>) Admin
- ul.nav.navbar-nav.navbar-right
- li(ng-hide='nav.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: nav.isActive("/signup")}'<% } %>)
+ ul.$ctrl.navbar-$ctrl.navbar-right
+ li(ng-hide='$ctrl.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: $ctrl.isActive("/signup")}'<% } %>)
a(<% if (filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) Sign up
- li(ng-hide='nav.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: nav.isActive("/login")}'<% } %>)
+ li(ng-hide='$ctrl.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: $ctrl.isActive("/login")}'<% } %>)
a(<% if (filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) Login
- li(ng-show='nav.isLoggedIn()')
- p.navbar-text Hello {{ nav.getCurrentUser().name }}
+ li(ng-show='$ctrl.isLoggedIn()')
+ p.navbar-text Hello {{ $ctrl.getCurrentUser().name }}
- li(ng-show='nav.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: nav.isActive("/settings")}'<% } %>)
+ li(ng-show='$ctrl.isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: $ctrl.isActive("/settings")}'<% } %>)
a(<% if (filters.uirouter) { %>ui-sref='settings'<% } else { %>href='/settings'<% } %>)
span.glyphicon.glyphicon-cog
- li(ng-show='nav.isLoggedIn()')
+ li(ng-show='$ctrl.isLoggedIn()')
a(<% if (filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %>
diff --git a/templates/app/client/components/navbar/navbar.controller.js b/templates/app/client/components/navbar/navbar.component.js
similarity index 77%
rename from templates/app/client/components/navbar/navbar.controller.js
rename to templates/app/client/components/navbar/navbar.component.js
index 12ae0b351..b91a3cabd 100644
--- a/templates/app/client/components/navbar/navbar.controller.js
+++ b/templates/app/client/components/navbar/navbar.component.js
@@ -1,17 +1,18 @@
'use strict';
-class NavbarController {
+export class NavbarComponent {
//start-non-standard
menu = [{
'title': 'Home',
<% if (filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %>
}];
-
+
isCollapsed = true;
//end-non-standard
<%_ if(filters.ngroute || filters.auth) { _%>
constructor(<% if(!filters.uirouter) { %>$location<% } if(!filters.uirouter && filters.auth) { %>, <% } if (filters.auth) { %>Auth<% } %>) {
+ 'ngInject';
<%_ if(!filters.uirouter) { _%>
this.$location = $location;
<%_ } _%>
@@ -28,5 +29,9 @@ class NavbarController {
}<% } %>
}
-angular.module('<%= scriptAppName %>')
- .controller('NavbarController', NavbarController);
+export default angular.module('directives.navbar', [])
+ .component('navbar', {
+ template: require('./navbar.html'),
+ controller: NavbarComponent
+ })
+ .name;
diff --git a/templates/app/client/components/navbar/navbar.directive.js b/templates/app/client/components/navbar/navbar.directive.js
deleted file mode 100644
index 3ae1a4a57..000000000
--- a/templates/app/client/components/navbar/navbar.directive.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>')
- .directive('navbar', () => ({
- templateUrl: 'components/navbar/navbar.html',
- restrict: 'E',
- controller: 'NavbarController',
- controllerAs: 'nav'
- }));
diff --git a/templates/app/client/components/oauth-buttons(oauth)/index.js b/templates/app/client/components/oauth-buttons(oauth)/index.js
new file mode 100644
index 000000000..17a07294a
--- /dev/null
+++ b/templates/app/client/components/oauth-buttons(oauth)/index.js
@@ -0,0 +1,21 @@
+'use strict';
+
+export function OauthButtonsController($window) {
+ this.loginOauth = function(provider) {
+ $window.location.href = '/auth/' + provider;
+ };
+}
+
+export default angular.module('<%= scriptAppName %>.oauthButtons', [])
+ .directive('oauthButtons', function() {
+ return {
+ template: require('./oauth-buttons.html'),
+ restrict: 'EA',
+ controller: OauthButtonsController,
+ controllerAs: 'OauthButtons',
+ scope: {
+ classes: '@'
+ }
+ };
+ })
+ .name;
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.js b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.js
deleted file mode 100644
index 36d5d6467..000000000
--- a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.js
+++ /dev/null
@@ -1,8 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>')
- .controller('OauthButtonsCtrl', function($window) {
- this.loginOauth = function(provider) {
- $window.location.href = '/auth/' + provider;
- };
- });
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.spec.js b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.spec.js
index 144745e5e..721792277 100644
--- a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.spec.js
+++ b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.controller.spec.js
@@ -1,11 +1,17 @@
'use strict';
-describe('Controller: OauthButtonsCtrl', function() {
+import {OauthButtonsController} from './index';
- // load the controller's module
- beforeEach(module('<%= scriptAppName %>'));
+describe('Controller: OauthButtonsController', function() {
+
+ var controller, $window;
- var OauthButtonsCtrl, $window;
+ beforeEach(() => {
+ angular.module('test', [])
+ .controller('OauthButtonsController', OauthButtonsController);
+ });
+ // load the controller's module
+ beforeEach(angular.mock.module('test'));
// Initialize the controller and a mock $window
beforeEach(inject(function($controller) {
@@ -13,13 +19,13 @@ describe('Controller: OauthButtonsCtrl', function() {
location: {}
};
- OauthButtonsCtrl = $controller('OauthButtonsCtrl', {
+ controller = $controller('OauthButtonsController', {
$window: $window
});
}));
it('should attach loginOauth', function() {<% if (filters.jasmine) { %>
- expect(OauthButtonsCtrl.loginOauth).toEqual(jasmine.any(Function));<% } if (filters.mocha) { %>
- <%= expect() %>OauthButtonsCtrl.loginOauth<%= to() %>.be.a('function');<% } %>
+ expect(controller.loginOauth).toEqual(jasmine.any(Function));<% } if (filters.mocha) { %>
+ <%= expect() %>controller.loginOauth<%= to() %>.be.a('function');<% } %>
});
});
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.js b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.js
deleted file mode 100644
index 401f669e3..000000000
--- a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>')
- .directive('oauthButtons', function() {
- return {
- templateUrl: 'components/oauth-buttons/oauth-buttons.html',
- restrict: 'EA',
- controller: 'OauthButtonsCtrl',
- controllerAs: 'OauthButtons',
- scope: {
- classes: '@'
- }
- };
- });
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.spec.js b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.spec.js
index 14682cc6e..d0a3af7ab 100644
--- a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.spec.js
+++ b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.directive.spec.js
@@ -1,10 +1,13 @@
'use strict';
+import $ from 'sprint-js';
+import OauthButtons from './index';
+
describe('Directive: oauthButtons', function() {
// load the directive's module and view
- beforeEach(module('<%= scriptAppName %>'));
- beforeEach(module('components/oauth-buttons/oauth-buttons.html'));
+ beforeEach(angular.mock.module(OauthButtons));
+ // beforeEach(angular.mock.module('components/oauth-buttons/oauth-buttons.html'));
var element, parentScope, elementScope;
@@ -23,8 +26,8 @@ describe('Directive: oauthButtons', function() {
it('should contain anchor buttons', function() {
compileDirective('
');<% if (filters.jasmine) { %>
- expect(element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>').length).toBeGreaterThan(0);<% } if (filters.mocha) { %>
- <%= expect() %>element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>').length<%= to() %>.be.at.least(1);<% } %>
+ expect($(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>').length).toBeGreaterThan(0);<% } if (filters.mocha) { %>
+ <%= expect() %>$(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>').length<%= to() %>.be.at.least(1);<% } %>
});
it('should evaluate and bind the classes attribute to scope.classes', function() {
@@ -39,13 +42,13 @@ describe('Directive: oauthButtons', function() {
// Add classes
elementScope.classes = 'testClass1 testClass2';
elementScope.$digest();<% if (filters.jasmine) { %>
- expect(element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length).toBeGreaterThan(0);<% } if (filters.mocha) { %>
- <%= expect() %>element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length<%= to() %>.be.at.least(1);<% } %>
+ expect($(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length).toBeGreaterThan(0);<% } if (filters.mocha) { %>
+ <%= expect() %>$(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length<%= to() %>.be.at.least(1);<% } %>
// Remove classes
elementScope.classes = '';
elementScope.$digest();<% if (filters.jasmine) { %>
- expect(element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length).toEqual(0);<% } if (filters.mocha) { %>
- <%= expect() %>element.find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length<%= to() %>.equal(0);<% } %>
+ expect($(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length).toEqual(0);<% } if (filters.mocha) { %>
+ <%= expect() %>$(element[0]).find('a.btn<% if (filters.bootstrap) { %>.btn-social<% } %>.testClass1.testClass2').length<%= to() %>.equal(0);<% } %>
});
});
diff --git a/templates/app/client/components/socket(socketio)/socket.service.js b/templates/app/client/components/socket(socketio)/socket.service.js
index 97e5dfee6..bcaa55b18 100644
--- a/templates/app/client/components/socket(socketio)/socket.service.js
+++ b/templates/app/client/components/socket(socketio)/socket.service.js
@@ -1,8 +1,8 @@
-/* global io */
'use strict';
-angular.module('<%= scriptAppName %>')
- .factory('socket', function(socketFactory) {
+import io from 'socket.io-client';
+
+function Socket(socketFactory) {
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
@@ -68,4 +68,8 @@ angular.module('<%= scriptAppName %>')
socket.removeAllListeners(modelName + ':remove');
}
};
- });
+ }
+
+export default angular.module('<%= scriptAppName %>.socket', [])
+ .factory('socket', Socket)
+ .name;
diff --git a/templates/app/client/components/util/util.module.js b/templates/app/client/components/util/util.module.js
index 690b12456..3e93a69b9 100644
--- a/templates/app/client/components/util/util.module.js
+++ b/templates/app/client/components/util/util.module.js
@@ -1,3 +1,6 @@
'use strict';
+import {UtilService} from './util.service';
-angular.module('<%= scriptAppName %>.util', []);
+export default angular.module('<%= scriptAppName %>.util', [])
+ .factory('Util', UtilService)
+ .name;
diff --git a/templates/app/client/components/util/util.service.js b/templates/app/client/components/util/util.service.js
index 4ce50a841..39b108988 100644
--- a/templates/app/client/components/util/util.service.js
+++ b/templates/app/client/components/util/util.service.js
@@ -1,11 +1,10 @@
'use strict';
-(function() {
-
/**
* The Util service is for thin, globally reusable, utility functions
*/
-function UtilService($window) {
+export function UtilService($window) {
+ 'ngInject';
var Util = {
/**
* Return a callback or noop function
@@ -50,7 +49,7 @@ function UtilService($window) {
origins = origins.filter(function(o) {
let hostnameCheck = url.hostname === o.hostname;
let protocolCheck = url.protocol === o.protocol;
- // 2nd part of the special treatment for IE fix (see above):
+ // 2nd part of the special treatment for IE fix (see above):
// This part is when using well-known ports 80 or 443 with IE,
// when $window.location.port==='' instead of the real port number.
// Probably the same cause as this IE bug: https://goo.gl/J9hRta
@@ -63,8 +62,3 @@ function UtilService($window) {
return Util;
}
-
-angular.module('<%= scriptAppName %>.util')
- .factory('Util', UtilService);
-
-})();
diff --git a/templates/app/client/index.html b/templates/app/client/index.html
deleted file mode 100644
index 807f85068..000000000
--- a/templates/app/client/index.html
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <% if (filters.ngroute) { %>
<% } %><% if (filters.uirouter) { %>
<% } %>
-
-
-
-
-
-
-
-
- <% if (filters.socketio) { %>
- <% } %>
-
-
-
-
-
-
-
-
diff --git a/templates/app/client/polyfills.js b/templates/app/client/polyfills.js
new file mode 100644
index 000000000..83e5dbb13
--- /dev/null
+++ b/templates/app/client/polyfills.js
@@ -0,0 +1,30 @@
+// Polyfills
+// (these modules are what are in 'angular2/bundles/angular2-polyfills' so don't use that here)
+
+// import 'ie-shim'; // Internet Explorer
+// import 'es6-shim';
+// import 'es6-promise';
+// import 'es7-reflect-metadata';
+
+// Prefer CoreJS over the polyfills above
+import 'core-js/es6';
+import 'core-js/es7/reflect';
+// require('zone.js/dist/zone');
+
+<%_ if(filters.ts) { _%>
+// Typescript emit helpers polyfill
+import 'ts-helpers';<% } %>
+
+if(!ENV) {
+ var ENV = 'development';
+}
+
+if(ENV === 'production') {
+ // Production
+} else {
+ // Development
+
+ Error.stackTraceLimit = Infinity;
+
+ // require('zone.js/dist/long-stack-trace-zone');
+}
diff --git a/templates/app/e2e/account(auth)/login/login.spec(mocha).js b/templates/app/e2e/account(auth)/login/login.spec(mocha).js
index 08758dc07..b7cce6aa6 100644
--- a/templates/app/e2e/account(auth)/login/login.spec(mocha).js
+++ b/templates/app/e2e/account(auth)/login/login.spec(mocha).js
@@ -56,12 +56,18 @@ describe('Login View', function() {
describe('with local auth', function() {
it('should login a user and redirecting to "/"', function() {
- page.login(testUser);
-
- var navbar = require('../../components/navbar/navbar.po');
-
- <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
- <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
+ page.login(testUser).then(() => {
+ var currentUrl;
+ return browser.getCurrentUrl().then(url => {
+ currentUrl = url;
+ browser.wait(() => browser.getCurrentUrl().then(url => url !== currentUrl));
+ }).then(() => {
+ var navbar = require('../../components/navbar/navbar.po');
+
+ <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
+ <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
+ });
+ });
});
describe('and invalid credentials', function() {
diff --git a/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js b/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js
index d1b170272..708fd0172 100644
--- a/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js
+++ b/templates/app/e2e/account(auth)/logout/logout.spec(mocha).js
@@ -38,17 +38,23 @@ describe('Logout View', function() {
describe('with local auth', function() {
it('should logout a user and redirecting to "/"', function() {
- var navbar = require('../../components/navbar/navbar.po');
+ var currentUrl;
+ return browser.getCurrentUrl().then(url => {
+ currentUrl = url;
+ browser.wait(() => browser.getCurrentUrl().then(url => url !== currentUrl));
+ }).then(() => {
+ var navbar = require('../../components/navbar/navbar.po');
- <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
- <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
+ <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
+ <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
- browser.get(config.baseUrl + '/logout');
+ browser.get(config.baseUrl + '/logout');
- navbar = require('../../components/navbar/navbar.po');
+ navbar = require('../../components/navbar/navbar.po');
- <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
- <%= expect() %>navbar.navbarAccountGreeting.isDisplayed()<%= to() %>.eventually.equal(false);
+ <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
+ <%= expect() %>navbar.navbarAccountGreeting.isDisplayed()<%= to() %>.eventually.equal(false);
+ });
});
});
diff --git a/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js b/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js
index f7debbe47..faadb92ad 100644
--- a/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js
+++ b/templates/app/e2e/account(auth)/signup/signup.spec(mocha).js
@@ -62,10 +62,16 @@ describe('Signup View', function() {
it('should signup a new user, log them in, and redirecting to "/"', function() {
page.signup(testUser);
- var navbar = require('../../components/navbar/navbar.po');
-
- <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
- <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
+ var currentUrl;
+ return browser.getCurrentUrl().then(url => {
+ currentUrl = url;
+ browser.wait(() => browser.getCurrentUrl().then(url => url !== currentUrl));
+ }).then(function () {
+ var navbar = require('../../components/navbar/navbar.po');
+
+ <%= expect() %>browser.getCurrentUrl()<%= to() %>.eventually.equal(config.baseUrl + '/');
+ <%= expect() %>navbar.navbarAccountGreeting.getText()<%= to() %>.eventually.equal('Hello ' + testUser.name);
+ });
});
describe('and invalid credentials', function() {
diff --git a/templates/app/gulpfile.babel(gulp).js b/templates/app/gulpfile.babel(gulp).js
index 16582400f..523fd99ad 100644
--- a/templates/app/gulpfile.babel(gulp).js
+++ b/templates/app/gulpfile.babel(gulp).js
@@ -6,6 +6,7 @@ import del from 'del';
import gulp from 'gulp';
import grunt from 'grunt';
import path from 'path';
+import through2 from 'through2';
import gulpLoadPlugins from 'gulp-load-plugins';
import http from 'http';
import open from 'open';
@@ -17,11 +18,17 @@ import runSequence from 'run-sequence';
import {protractor, webdriver_update} from 'gulp-protractor';
import {Instrumenter} from 'isparta';<% if(filters.stylus) { %>
import nib from 'nib';<% } %>
+import webpack from 'webpack-stream';
+import makeWebpackConfig from './webpack.make';
var plugins = gulpLoadPlugins();
var config;
+const webpackDevConfig = makeWebpackConfig({ DEV: true });
+const webpackE2eConfig = makeWebpackConfig({ E2E: true });
+const webpackDistConfig = makeWebpackConfig({ BUILD: true });
+const webpackTestConfig = makeWebpackConfig({ TEST: true });
-const clientPath = require('./bower.json').appPath || 'client';
+const clientPath = 'client';
const serverPath = 'server';
const paths = {
client: {
@@ -91,25 +98,6 @@ function whenServerReady(cb) {
100);
}
-function sortModulesFirst(a, b) {
- var module = /\.module\.<%= scriptExt %>$/;
- var aMod = module.test(a.path);
- var bMod = module.test(b.path);
- // inject *.module.js first
- if (aMod === bMod) {
- // either both modules or both non-modules, so just sort normally
- if (a.path < b.path) {
- return -1;
- }
- if (a.path > b.path) {
- return 1;
- }
- return 0;
- } else {
- return (aMod ? -1 : 1);
- }
-}
-
/********************
* Reusable pipelines
********************/
@@ -118,7 +106,7 @@ let lintClientScripts = lazypipe()<% if(filters.babel) { %>
.pipe(plugins.jshint, `${clientPath}/.jshintrc`)
.pipe(plugins.jshint.reporter, 'jshint-stylish');<% } %><% if(filters.ts) { %>
.pipe(plugins.tslint, require(`./${clientPath}/tslint.json`))
- .pipe(plugins.tslint.report, 'verbose');<% } %>
+ .pipe(plugins.tslint.report, 'verbose', {emitError: false});<% } %>
let lintServerScripts = lazypipe()
.pipe(plugins.jshint, `${serverPath}/.jshintrc`)
@@ -128,24 +116,6 @@ let lintServerTestScripts = lazypipe()
.pipe(plugins.jshint, `${serverPath}/.jshintrc-spec`)
.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)<% } %>
- <%_ if(filters.css) { _%>
- .pipe(plugins.cleanCss, {processImportFrom: ['!fonts.googleapis.com']})<% } %>
- .pipe(plugins.autoprefixer, {browsers: ['last 1 version']})
- .pipe(plugins.sourcemaps.write, '.');<% if(filters.babel) { %>
-
-let transpileClient = lazypipe()
- .pipe(plugins.sourcemaps.init)
- .pipe(plugins.babel)
- .pipe(plugins.sourcemaps.write, '.');<% } %>
-
let transpileServer = lazypipe()
.pipe(plugins.sourcemaps.init)
.pipe(plugins.babel, {
@@ -211,20 +181,7 @@ gulp.task('env:prod', () => {
********************/
gulp.task('inject', cb => {
- runSequence(['inject:js', 'inject:css'<% if(!filters.css) { %>, 'inject:<%= styleExt %>'<% } %><% if(filters.ts) { %>, 'inject:tsconfig'<% } %>], cb);
-});
-
-gulp.task('inject:js', () => {
- return gulp.src(paths.client.mainView)
- .pipe(plugins.inject(
- gulp.src(_.union(paths.client.scripts, [<% if(filters.ts) { %>'client/app/app.constant.js', <% } %>`!${clientPath}/**/*.{spec,mock}.<%= scriptExt %>`, `!${clientPath}/app/app.<%= scriptExt %>`]), {read: false})
- .pipe(plugins.sort(sortModulesFirst)),
- {
- starttag: '',
- endtag: '',
- transform: (filepath) => ''
- }))
- .pipe(gulp.dest(clientPath));
+ runSequence(['inject:css'<% if(!filters.css) { %>, 'inject:<%= styleExt %>'<% } %><% if(filters.ts) { %>, 'inject:tsconfig'<% } %>], cb);
});<% if(filters.ts) { %>
function injectTsConfig(filesGlob, tsconfigPath){
@@ -247,7 +204,7 @@ gulp.task('inject:tsconfig', () => {
`${clientPath}/**/!(*.spec|*.mock).ts`,
`!${clientPath}/bower_components/**/*`,
`typings/main.d.ts`
- ],
+ ],
'./tsconfig.client.json');
});
@@ -256,7 +213,7 @@ gulp.task('inject:tsconfigTest', () => {
`${clientPath}/**/+(*.spec|*.mock).ts`,
`!${clientPath}/bower_components/**/*`,
`typings/main.d.ts`
- ],
+ ],
'./tsconfig.client.test.json');
});<% } %>
@@ -293,7 +250,33 @@ gulp.task('inject:<%= styleExt %>', () => {
}
}))
.pipe(gulp.dest(`${clientPath}/app`));
-});<% } %><% if(filters.ts) { %>
+});<% } %>
+
+gulp.task('webpack:dev', function() {
+ return gulp.src(webpackDevConfig.entry.app)
+ .pipe(plugins.plumber())
+ .pipe(webpack(webpackDevConfig))
+ .pipe(gulp.dest('.tmp'))
+ .pipe(plugins.livereload());
+});
+
+gulp.task('webpack:dist', function() {
+ return gulp.src(webpackDistConfig.entry.app)
+ .pipe(webpack(webpackDistConfig))
+ .pipe(gulp.dest(`${paths.dist}/client`));
+});
+
+gulp.task('webpack:test', function() {
+ return gulp.src(webpackTestConfig.entry.app)
+ .pipe(webpack(webpackTestConfig))
+ .pipe(gulp.dest('.tmp'));
+});
+
+gulp.task('webpack:e2e', function() {
+ return gulp.src(webpackE2eConfig.entry.app)
+ .pipe(webpack(webpackE2eConfig))
+ .pipe(gulp.dest('.tmp'));
+});<% if(filters.ts) { %>
// Install DefinitelyTyped TypeScript definition files
gulp.task('typings', () => {
@@ -314,28 +297,6 @@ gulp.task('styles', () => {
gulp.task('copy:constant', ['constant'], () => {
return gulp.src(`${clientPath}/app/app.constant.js`, { dot: true })
.pipe(gulp.dest('.tmp/app'));
-})
-
-gulp.task('transpile:client', ['typings', 'copy:constant'], () => {
- return gulp.src(['client/{app,components}/**/!(*.spec|*.mock).ts', 'typings/main.d.ts'])
- .pipe(plugins.sourcemaps.init())
- .pipe(plugins.typescript()).js
- .pipe(plugins.sourcemaps.write('.'))
- .pipe(gulp.dest('.tmp'));
-});
-
-gulp.task('transpile:client:test', ['typings'], () => {
- return gulp.src(['client/{app,components}/**/+(*.spec|*.mock).ts', 'typings/main.d.ts'])
- .pipe(plugins.sourcemaps.init())
- .pipe(plugins.typescript()).js
- .pipe(plugins.sourcemaps.write('.'))
- .pipe(gulp.dest('.tmp/test'));
-});<% } %><% if(filters.babel) { %>
-
-gulp.task('transpile:client', () => {
- return gulp.src(paths.client.scripts)
- .pipe(transpileClient())
- .pipe(gulp.dest('.tmp'));
});<% } %>
gulp.task('transpile:server', () => {
@@ -416,44 +377,51 @@ gulp.task('watch', () => {
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)<% if(filters.jade) { %>
- .pipe(plugins.jade())
- .pipe(gulp.dest('.tmp'))<% } %>
- .pipe(plugins.plumber())
- .pipe(plugins.livereload());<% if(filters.babel) { %>
-
- plugins.watch(paths.client.scripts) //['inject:js']
- .pipe(plugins.plumber())
- .pipe(transpileClient())
- .pipe(gulp.dest('.tmp'))
- .pipe(plugins.livereload());<% } %><% if(filters.ts) { %>
-
- gulp.watch(paths.client.scripts, ['lint:scripts:client', 'transpile:client']);<% } %>
-
plugins.watch(_.union(paths.server.scripts, testFiles))
.pipe(plugins.plumber())
.pipe(lintServerScripts())
.pipe(plugins.livereload());
- gulp.watch('bower.json', ['wiredep:client']);
+ plugins.watch(_.union(paths.server.test.unit, paths.server.test.integration))
+ .pipe(plugins.plumber())
+ .pipe(lintServerTestScripts());
});
gulp.task('serve', cb => {
- runSequence(['clean:tmp', 'constant', 'env:all'<% if(filters.ts) { %>, 'typings'<% } %>],
- ['lint:scripts', 'inject'<% if(filters.jade) { %>, 'jade'<% } %>],
- ['wiredep:client'],
- ['transpile:client', 'styles'],
+ runSequence(
+ [
+ 'clean:tmp',
+ 'lint:scripts',
+ 'constant',
+ 'inject',
+ 'copy:fonts:dev',
+ 'env:all'<% if(filters.ts) { %>,
+ 'typings'<% } %>
+ ],
+ // 'webpack:dev',
['start:server', 'start:client'],
'watch',
- cb);
+ cb
+ );
+});
+
+gulp.task('serve:debug', cb => {
+ runSequence(
+ [
+ 'clean:tmp',
+ 'lint:scripts',
+ 'constant',
+ 'inject',
+ 'copy:fonts:dev',
+ 'env:all'<% if(filters.ts) { %>,
+ 'typings'<% } %>
+ ],
+ 'webpack:dev',
+ 'start:inspector',
+ ['start:server:debug', 'start:client'],
+ 'watch',
+ cb
+ );
});
gulp.task('serve:dist', cb => {
@@ -465,17 +433,6 @@ gulp.task('serve:dist', cb => {
cb);
});
-gulp.task('serve:debug', cb => {
- runSequence(['clean:tmp', 'constant'<% if(filters.ts) { %>, 'typings'<% } %>],
- ['lint:scripts', 'inject'<% if(filters.jade) { %>, 'jade'<% } %>],
- ['wiredep:client'],
- ['transpile:client', 'styles'],
- 'start:inspector',
- ['start:server:debug', 'start:client'],
- 'watch',
- cb);
-});
-
gulp.task('test', cb => {
return runSequence('test:server', 'test:client', cb);
});
@@ -500,124 +457,94 @@ gulp.task('mocha:integration', () => {
.pipe(mocha());
});
-gulp.task('test:client', ['wiredep:test', 'constant'<% if(filters.ts) { %>, 'transpile:client', 'transpile:client:test'<% } %>], (done) => {
+gulp.task('mocha:coverage', cb => {
+ runSequence('coverage:pre',
+ 'env:all',
+ 'env:test',
+ 'coverage:unit',
+ 'coverage:integration',
+ cb);
+});
+
+gulp.task('coverage:pre', () => {
+ return gulp.src(paths.server.scripts)
+ // Covering files
+ .pipe(plugins.istanbul({
+ instrumenter: Instrumenter, // Use the isparta instrumenter (code coverage for ES6)
+ includeUntested: true
+ }))
+ // Force `require` to return covered files
+ .pipe(plugins.istanbul.hookRequire());
+});
+
+gulp.task('coverage:unit', () => {
+ return gulp.src(paths.server.test.unit)
+ .pipe(mocha())
+ .pipe(istanbul())
+ // Creating the reports after tests ran
+});
+
+gulp.task('coverage:integration', () => {
+ return gulp.src(paths.server.test.integration)
+ .pipe(mocha())
+ .pipe(istanbul())
+ // Creating the reports after tests ran
+});
+
+// Downloads the selenium webdriver
+gulp.task('webdriver_update', webdriver_update);
+
+gulp.task('test:e2e', ['webpack:e2e', 'constant', 'env:all', 'env:test', 'start:server', 'webdriver_update'], cb => {
+ gulp.src(paths.client.e2e)
+ .pipe(protractor({
+ configFile: 'protractor.conf.js',
+ })).on('error', err => {
+ console.log(err)
+ }).on('end', () => {
+ process.exit();
+ });
+});
+
+gulp.task('test:client', ['constant'], done => {
new KarmaServer({
configFile: `${__dirname}/${paths.karma}`,
singleRun: true
}, done).start();
});
-// inject bower components
-gulp.task('wiredep:client', () => {
- return gulp.src(paths.client.mainView)
- .pipe(wiredep({
- exclude: [<% if(filters.uibootstrap) { %>
- /bootstrap.js/,<% } %>
- '/json3/',
- '/es5-shim/'<% if(!filters.css) { %>,
- /font-awesome\.css/<% if(filters.bootstrap) { %>,
- /bootstrap\.css/<% if(filters.sass) { %>,
- /bootstrap-sass-official/<% } if(filters.oauth) { %>,
- /bootstrap-social\.css/<% }}} %>
- ],
- ignorePath: clientPath
- }))
- .pipe(gulp.dest(`${clientPath}/`));
-});
-
-gulp.task('wiredep:test', () => {
- return gulp.src(paths.karma)
- .pipe(wiredep({
- exclude: [<% if(filters.uibootstrap) { %>
- /bootstrap.js/,<% } %>
- '/json3/',
- '/es5-shim/'<% if(!filters.css) { %>,
- /font-awesome\.css/<% if(filters.bootstrap) { %>,
- /bootstrap\.css/<% if(filters.sass) { %>,
- /bootstrap-sass-official/<% } if(filters.oauth) { %>,
- /bootstrap-social\.css/<% }}} %>
- ],
- devDependencies: true
- }))
- .pipe(gulp.dest('./'));
-});
-
/********************
* Build
********************/
-//FIXME: looks like font-awesome isn't getting loaded
gulp.task('build', cb => {
runSequence(
[
'clean:dist',
'clean:tmp'
- ],<% if(filters.jade) { %>
- 'jade',<% } %>
+ ],
'inject',
- 'wiredep:client',<% if(filters.ts) { %>
- 'typings',<% } %>
[
'transpile:client',
'transpile:server'
],
[
'build:images',
+ 'generate-favicon',
+ 'typings'
+ ],
+ [
'copy:extras',
- 'copy:fonts',
'copy:assets',
+ 'copy:fonts:dist',
'copy:server',
- 'build:client'
+ 'webpack:dist'
],
+ 'revReplaceWebpack',
cb);
});
gulp.task('clean:dist', () => del([`${paths.dist}/!(.git*|.openshift|Procfile)**`], {dot: true}));
-gulp.task('build:client', ['styles', 'html', 'constant', 'build:images'], () => {
- var manifest = gulp.src(`${paths.dist}/${clientPath}/assets/rev-manifest.json`);
-
- var appFilter = plugins.filter('**/app.js', {restore: true});
- var jsFilter = plugins.filter('**/*.js', {restore: true});
- var cssFilter = plugins.filter('**/*.css', {restore: true});
- var htmlBlock = plugins.filter(['**/*.!(html)'], {restore: true});
-
- return gulp.src(paths.client.mainView)
- .pipe(plugins.useref())
- .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.cleanCss({
- processImportFrom: ['!fonts.googleapis.com']
- }))
- .pipe(cssFilter.restore)
- .pipe(htmlBlock)
- .pipe(plugins.rev())
- .pipe(htmlBlock.restore)
- .pipe(plugins.revReplace({manifest}))
- .pipe(gulp.dest(`${paths.dist}/${clientPath}`));
-});
-
-gulp.task('html', function() {<% if(filters.jade) { %>
- return gulp.src(`.tmp/{app,components}/**/*.html`)<% } else { %>
- return gulp.src(`${clientPath}/{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(`./${serverPath}/config/environment/shared`);
return plugins.ngConstant({
@@ -649,6 +576,12 @@ gulp.task('build:images', () => {
.pipe(gulp.dest(`${paths.dist}/${clientPath}/assets`));
});
+gulp.task('revReplaceWebpack', function() {
+ return gulp.src('dist/client/app.*.js')
+ .pipe(plugins.revReplace({manifest: gulp.src(paths.client.assets.revManifest)}))
+ .pipe(gulp.dest('dist/client'));
+});
+
gulp.task('copy:extras', () => {
return gulp.src([
`${clientPath}/favicon.ico`,
@@ -658,10 +591,37 @@ gulp.task('copy:extras', () => {
.pipe(gulp.dest(`${paths.dist}/${clientPath}`));
});
-gulp.task('copy:fonts', () => {<% if(filters.bootstrap) { %>
- return gulp.src(`${clientPath}/bower_components/{bootstrap,font-awesome}/fonts/**/*`, { dot: true })<% } else { %>
- return gulp.src(`${clientPath}/bower_components/font-awesome/fonts/**/*`, { dot: true })<% } %>
- .pipe(gulp.dest(`${paths.dist}/${clientPath}/bower_components`));
+/**
+ * turns 'boostrap/fonts/font.woff' into 'boostrap/font.woff'
+ */
+function flatten() {
+ return through2.obj(function(file, enc, next) {
+ if(!file.isDirectory()) {
+ try {
+ let dir = path.dirname(file.relative).split(path.sep)[0];
+ let fileName = path.normalize(path.basename(file.path));
+ file.path = path.join(file.base, path.join(dir, fileName));
+ this.push(file);
+ } catch(e) {
+ this.emit('error', new Error(e));
+ }
+ }
+ next();
+ });
+}
+gulp.task('copy:fonts:dev', () => {
+ <%_ if(filters.bootstrap) { _%>
+ return gulp.src('node_modules/{bootstrap,font-awesome}/fonts/*')<% } else { %>
+ return gulp.src('node_modules/font-awesome/fonts/*')<% } %>
+ .pipe(flatten())
+ .pipe(gulp.dest(`${clientPath}/assets/fonts`));
+});
+gulp.task('copy:fonts:dist', () => {
+ <%_ if(filters.bootstrap) { _%>
+ return gulp.src('node_modules/{bootstrap,font-awesome}/fonts/*')<% } else { %>
+ return gulp.src('node_modules/font-awesome/fonts/*')<% } %>
+ .pipe(flatten())
+ .pipe(gulp.dest(`${paths.dist}/${clientPath}/assets/fonts`));
});
gulp.task('copy:assets', () => {
@@ -678,54 +638,6 @@ gulp.task('copy:server', () => {
.pipe(gulp.dest(paths.dist));
});
-gulp.task('coverage:pre', () => {
- return gulp.src(paths.server.scripts)
- // Covering files
- .pipe(plugins.istanbul({
- instrumenter: Instrumenter, // Use the isparta instrumenter (code coverage for ES6)
- includeUntested: true
- }))
- // Force `require` to return covered files
- .pipe(plugins.istanbul.hookRequire());
-});
-
-gulp.task('coverage:unit', () => {
- return gulp.src(paths.server.test.unit)
- .pipe(mocha())
- .pipe(istanbul())
- // Creating the reports after tests ran
-});
-
-gulp.task('coverage:integration', () => {
- return gulp.src(paths.server.test.integration)
- .pipe(mocha())
- .pipe(istanbul())
- // Creating the reports after tests ran
-});
-
-gulp.task('mocha:coverage', cb => {
- runSequence('coverage:pre',
- 'env:all',
- 'env:test',
- 'coverage:unit',
- 'coverage:integration',
- cb);
-});
-
-// Downloads the selenium webdriver
-gulp.task('webdriver_update', webdriver_update);
-
-gulp.task('test:e2e', ['env:all', 'env:test', 'start:server', 'webdriver_update'], cb => {
- gulp.src(paths.client.e2e)
- .pipe(protractor({
- configFile: 'protractor.conf.js',
- })).on('error', err => {
- console.log(err)
- }).on('end', () => {
- process.exit();
- });
-});
-
/********************
* Grunt ported tasks
********************/
diff --git a/templates/app/karma.conf.js b/templates/app/karma.conf.js
index 35190d50e..157c7a90e 100644
--- a/templates/app/karma.conf.js
+++ b/templates/app/karma.conf.js
@@ -1,5 +1,8 @@
// Karma configuration
-// http://karma-runner.github.io/0.10/config/configuration-file.html
+// http://karma-runner.github.io/0.13/config/configuration-file.html
+/*eslint-env node*/
+
+import makeWebpackConfig from './webpack.make';
module.exports = function(config) {
config.set({
@@ -17,45 +20,47 @@ module.exports = function(config) {
},<% } %>
// list of files / patterns to load in the browser
- files: [
- // bower:js
- // endbower<% if (filters.socketio) { %>
- 'node_modules/socket.io-client/socket.io.js',<% } %><% if(filters.ts) { %>
- '.tmp/app/app.js',
- '.tmp/{app,components}/**/*.module.js',
- '.tmp/{app,components}/**/*.js',
- '.tmp/test/**/*.js',<% } %><% if(filters.babel) { %>
- 'client/app/app.js',
- 'client/{app,components}/**/*.module.js',
- 'client/{app,components}/**/*.js',<% } %>
- 'client/{app,components}/**/*.<%= filters.jade ? '{jade,html}' : 'html' %>'
- ],
+ files: ['spec.js'],
preprocessors: {
- '**/*.html': 'ng-html2js',<% if (filters.jade) { %>
- '**/*.jade': 'ng-jade2js',<% } if (filters.babel) { %>
- 'client/{app,components}/**/*.js': 'babel'<% } %>
+ 'spec.js': ['webpack']
},
- ngHtml2JsPreprocessor: {
- stripPrefix: 'client/'
- },<% if (filters.jade) { %>
-
- ngJade2JsPreprocessor: {
- stripPrefix: 'client/'
- },<% } if (filters.babel) { %>
-
- babelPreprocessor: {
- options: {
- sourceMap: 'inline'
- },
- filename: function (file) {
- return file.originalPath.replace(/\.js$/, '.es5.js');
- },
- sourceFileName: function (file) {
- return file.originalPath;
- }
- },<% } %>
+ webpack: makeWebpackConfig({ TEST: true }),
+
+ webpackMiddleware: {
+ // webpack-dev-middleware configuration
+ // i. e.
+ noInfo: true
+ },
+
+ coverageReporter: {
+ reporters: [{
+ type: 'html', //produces a html document after code is run
+ subdir: 'client'
+ }, {
+ type: 'json',
+ subdir: '.',
+ file: 'client-coverage.json'
+ }],
+ dir: 'coverage/' //path to created html doc
+ },
+
+ plugins: [
+ require('karma-chai-plugins'),
+ require('karma-chrome-launcher'),
+ require('karma-coverage'),
+ require('karma-firefox-launcher'),
+ <%_ if(filters.mocha) { _%>
+ require('karma-mocha'),
+ require('karma-chai-plugins'),<% } %>
+ <%_ if(filters.jasmine) { _%>
+ require('karma-jasmine'),<% } %>
+ require('karma-spec-reporter'),
+ require('karma-phantomjs-launcher'),
+ require('karma-script-launcher'),
+ require('karma-webpack')
+ ],
// list of files / patterns to exclude
exclude: [],
@@ -74,7 +79,7 @@ module.exports = function(config) {
// - junit
// - growl
// - coverage
- reporters: ['spec'],
+ reporters: ['spec', 'coverage'],
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
diff --git a/templates/app/server/config/express.js b/templates/app/server/config/express.js
index 2e4b8b1cb..db906ba85 100644
--- a/templates/app/server/config/express.js
+++ b/templates/app/server/config/express.js
@@ -86,13 +86,58 @@ export default function(app) {
}
if ('development' === env) {
- app.use(require('connect-livereload')({
+ const webpackDevMiddleware = require('webpack-dev-middleware');
+ const webpack = require('webpack');
+ const makeWebpackConfig = require('../../webpack.make');
+ const webpackConfig = makeWebpackConfig({ DEV: true });
+ const compiler = webpack(webpackConfig);
+
+ const pkgConfig = require('../../package.json');
+ const livereloadServer = require('tiny-lr')();
+ var livereloadServerConfig = {
ignore: [
/^\/api\/(.*)/,
/\.js(\?.*)?$/, /\.css(\?.*)?$/, /\.svg(\?.*)?$/, /\.ico(\?.*)?$/, /\.woff(\?.*)?$/,
/\.png(\?.*)?$/, /\.jpg(\?.*)?$/, /\.jpeg(\?.*)?$/, /\.gif(\?.*)?$/, /\.pdf(\?.*)?$/
- ]
+ ],
+ port: (pkgConfig.livereload || {}).port
+ };
+ var triggerLiveReloadChanges = function() {
+ livereloadServer.changed({
+ body: {
+ files: [webpackConfig.output.path + webpackConfig.output.filename]
+ }
+ });
+ };
+ if(livereloadServerConfig.port) {
+ livereloadServer.listen(livereloadServerConfig.port, triggerLiveReloadChanges);
+ } else {
+ /**
+ * Get free port for livereload
+ * server
+ */
+ livereloadServerConfig.port = require('http').createServer().listen(function() {
+ /*eslint no-invalid-this:0*/
+ this.close();
+ livereloadServer.listen(livereloadServerConfig.port, triggerLiveReloadChanges);
+ }).address().port;
+ }
+
+ /**
+ * On change compilation of bundle
+ * trigger livereload change event
+ */
+ compiler.plugin('done', triggerLiveReloadChanges);
+
+ app.use(webpackDevMiddleware(compiler, {
+ stats: {
+ colors: true,
+ timings: true,
+ chunks: false
+ }
}));
+
+ app.use(require('connect-livereload')(livereloadServerConfig));
}
if ('development' === env || 'test' === env) {
diff --git a/templates/app/spec.js b/templates/app/spec.js
new file mode 100644
index 000000000..13ecbc155
--- /dev/null
+++ b/templates/app/spec.js
@@ -0,0 +1,22 @@
+'use strict';
+/*eslint-env node*/
+var testsContext;
+
+require('babel-polyfill');
+require('angular');
+require('angular-mocks');
+<%_ if(filters.uirouter) { _%>
+require('./client/components/ui-router/ui-router.mock');<% } %>
+<%_ if(filters.socketio) { _%>
+require('./client/components/socket/socket.mock');<% } %>
+
+testsContext = require.context('./client', true, /\.spec\.js$/);
+// var keys = testsContext.keys();
+// console.log(keys);
+// console.log(keys[0]);
+// testsContext(keys[0]);
+// console.log(keys[1]);
+// testsContext(keys[1]);
+// console.log(keys[2]);
+// testsContext(keys[2]);
+testsContext.keys().forEach(testsContext);
diff --git a/templates/app/tsconfig.client(ts).json b/templates/app/tsconfig.client(ts).json
index e84a3e2fe..08ce37523 100644
--- a/templates/app/tsconfig.client(ts).json
+++ b/templates/app/tsconfig.client(ts).json
@@ -2,13 +2,24 @@
"compilerOptions": {
"sourceMap": true,
"rootDir": "./client",
+ "module": "commonjs",
"outDir": ".tmp",
+ "removeComments": false,
"target": "ES5"
},
+ "exclude": [
+ "node_modules",
+ "typings/main.d.ts",
+ "typings/main"
+ ],
"filesGlob": [
"client/{app,components}/**/!(*.spec).ts",
"typings/main.d.ts"
],
+ "awesomeTypescriptLoaderOptions": {
+ "resolveGlobs": true,
+ "forkChecker": true
+ },
"files": [
"client/app/account/account.ts",
"client/app/account/login/login.controller.ts",
diff --git a/templates/app/tsconfig.client.test(ts).json b/templates/app/tsconfig.client.test(ts).json
index 8263986dd..b704be74b 100644
--- a/templates/app/tsconfig.client.test(ts).json
+++ b/templates/app/tsconfig.client.test(ts).json
@@ -2,6 +2,7 @@
"compilerOptions": {
"sourceMap": true,
"rootDir": "./client",
+ "module": "commonjs",
"outDir": ".tmp/test"
},
"filesGlob": [
diff --git a/templates/app/typings(ts).json b/templates/app/typings(ts).json
index cfbd0f707..7f7c9795d 100644
--- a/templates/app/typings(ts).json
+++ b/templates/app/typings(ts).json
@@ -3,7 +3,10 @@
"angular": "registry:dt/angular#1.5.0+20160412133217",
"jquery": "github:DefinitelyTyped/DefinitelyTyped/jquery/jquery.d.ts#40c60850ad6c8175a62d5ab48c4e016ea5b3dffe",
"lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#40c60850ad6c8175a62d5ab48c4e016ea5b3dffe",
- "socket.io-client": "github:DefinitelyTyped/DefinitelyTyped/socket.io-client/socket.io-client.d.ts#40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
+ "socket.io-client": "github:DefinitelyTyped/DefinitelyTyped/socket.io-client/socket.io-client.d.ts#40c60850ad6c8175a62d5ab48c4e016ea5b3dffe",
+ "core-js": "registry:dt/core-js#0.0.0+20160317120654",
+ "node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#8cf8164641be73e8f1e652c2a5b967c7210b6729",
+ "webpack": "github:DefinitelyTyped/DefinitelyTyped/webpack/webpack.d.ts#95c02169ba8fa58ac1092422efbd2e3174a206f4"
},
"ambientDevDependencies": {
"angular-protractor": "github:DefinitelyTyped/DefinitelyTyped/angular-protractor/angular-protractor.d.ts#40c60850ad6c8175a62d5ab48c4e016ea5b3dffe",
diff --git a/templates/app/webpack.build.js b/templates/app/webpack.build.js
new file mode 100644
index 000000000..2bf43c071
--- /dev/null
+++ b/templates/app/webpack.build.js
@@ -0,0 +1,8 @@
+/**
+ * Webpack config for builds
+ */
+module.exports = require('./webpack.make')({
+ BUILD: true,
+ TEST: false,
+ DEV: false
+});
diff --git a/templates/app/webpack.dev.js b/templates/app/webpack.dev.js
new file mode 100644
index 000000000..491f6e946
--- /dev/null
+++ b/templates/app/webpack.dev.js
@@ -0,0 +1,8 @@
+/**
+ * Webpack config for development
+ */
+module.exports = require('./webpack.make')({
+ BUILD: false,
+ TEST: false,
+ DEV: true
+});
diff --git a/templates/app/webpack.make.js b/templates/app/webpack.make.js
new file mode 100644
index 000000000..56dc56e75
--- /dev/null
+++ b/templates/app/webpack.make.js
@@ -0,0 +1,394 @@
+'use strict';
+/*eslint-env node*/
+var webpack = require('webpack');
+var autoprefixer = require('autoprefixer');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+var HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
+var ExtractTextPlugin = require('extract-text-webpack-plugin');
+var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
+var fs = require('fs');
+var path = require('path');
+var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;
+
+module.exports = function makeWebpackConfig(options) {
+ /**
+ * Environment type
+ * BUILD is for generating minified builds
+ * TEST is for generating test builds
+ */
+ var BUILD = !!options.BUILD;
+ var TEST = !!options.TEST;
+ var E2E = !!options.E2E;
+ var DEV = !!options.DEV;
+
+ /**
+ * Config
+ * Reference: http://webpack.github.io/docs/configuration.html
+ * This is the object where all configuration gets set
+ */
+ var config = {};
+
+ /**
+ * Entry
+ * Reference: http://webpack.github.io/docs/configuration.html#entry
+ * Should be an empty object if it's generating a test build
+ * Karma will set this when it's a test build
+ */
+ if(TEST) {
+ config.entry = {};
+ } else {
+ config.entry = {
+ app: './client/app/app.<%= scriptExt %>',
+ polyfills: './client/polyfills.<%= scriptExt %>',
+ vendor: [
+ 'angular',
+ 'angular-animate',
+ 'angular-aria',
+ 'angular-cookies',
+ 'angular-resource',
+ <%_ if(filters.ngroute) { _%>
+ 'angular-route',<% } %>
+ 'angular-sanitize',
+ 'angular-socket-io',
+ 'angular-ui-bootstrap',
+ <%_ if(filters.uirouter) { _%>
+ 'angular-ui-router',<% } %>
+ 'lodash'
+ ]
+ };
+ }
+
+ /**
+ * Output
+ * Reference: http://webpack.github.io/docs/configuration.html#output
+ * Should be an empty object if it's generating a test build
+ * Karma will handle setting it up for you when it's a test build
+ */
+ if(TEST) {
+ config.output = {};
+ } else {
+ config.output = {
+ // Absolute output directory
+ path: BUILD ? path.join(__dirname, '/dist/client/') : path.join(__dirname, '/.tmp/'),
+
+ // Output path from the view of the page
+ // Uses webpack-dev-server in development
+ publicPath: BUILD || DEV || E2E ? '/' : `http://localhost:${8080}/`,
+ //publicPath: BUILD ? '/' : 'http://localhost:' + env.port + '/',
+
+ // Filename for entry points
+ // Only adds hash in build mode
+ filename: BUILD ? '[name].[hash].js' : '[name].bundle.js',
+
+ // Filename for non-entry points
+ // Only adds hash in build mode
+ chunkFilename: BUILD ? '[name].[hash].js' : '[name].bundle.js'
+ };
+ }
+
+ <%_ if(filters.ts) { _%>
+ config.resolve = {
+ modulesDirectories: ['node_modules'],
+ extensions: ['', '.js', '.ts']
+ };<% } %>
+
+ if(TEST) {
+ config.resolve = {
+ modulesDirectories: [
+ 'node_modules'
+ ],
+ extensions: ['', '.js', '.ts']
+ };
+ }
+
+ /**
+ * Devtool
+ * Reference: http://webpack.github.io/docs/configuration.html#devtool
+ * Type of sourcemap to use per build type
+ */
+ if(TEST) {
+ config.devtool = 'inline-source-map';
+ } else if(BUILD || DEV) {
+ config.devtool = 'source-map';
+ } else {
+ config.devtool = 'eval';
+ }
+
+ /**
+ * Loaders
+ * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
+ * List: http://webpack.github.io/docs/list-of-loaders.html
+ * This handles most of the magic responsible for converting modules
+ */
+ <%_ if(filters.sass) { _%>
+
+ config.sassLoader = {
+ outputStyle: 'compressed',
+ precision: 10,
+ sourceComments: false
+ };<% } %>
+
+ // Initialize module
+ config.module = {
+ preLoaders: [],
+ loaders: [{
+ // JS LOADER
+ // Reference: https://github.com/babel/babel-loader
+ // Transpile .js files using babel-loader
+ // Compiles ES6 and ES7 into ES5 code
+ test: /\.js$/,
+ loader: 'babel',
+ query: {comments: DEV || E2E},
+ include: [
+ path.resolve(__dirname, 'client/'),
+ path.resolve(__dirname, 'node_modules/lodash-es/')
+ ]
+ }, {
+ // TS LOADER
+ // Reference: https://github.com/s-panferov/awesome-typescript-loader
+ // Transpile .ts files using awesome-typescript-loader
+ test: /\.ts$/,
+ loader: 'awesome-typescript-loader',
+ exclude: [/\.(spec|e2e)\.ts$/],
+ query: {
+ tsconfig: path.resolve(__dirname, 'tsconfig.client.json')
+ },
+ include: [
+ path.resolve(__dirname, 'client/')
+ ]
+ }, {
+ // ASSET LOADER
+ // Reference: https://github.com/webpack/file-loader
+ // Copy png, jpg, jpeg, gif, svg, woff, woff2, ttf, eot files to output
+ // Rename the file using the asset hash
+ // Pass along the updated reference to your code
+ // You can add here any file extension you want to get copied to your output
+ test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
+ loader: 'file'
+ }, {
+ <%_ if(filters.sass) { _%>
+ // Jade LOADER
+ // Reference: https://github.com/webpack/jade-loader
+ // Allow loading jade throw js
+ test: /\.jade$/,
+ loaders: ['raw', 'jade']
+ }, {<% } %>
+ // HTML LOADER
+ // Reference: https://github.com/webpack/raw-loader
+ // Allow loading html through js
+ test: /\.html$/,
+ loader: 'raw'
+ }, {
+ // CSS LOADER
+ // Reference: https://github.com/webpack/css-loader
+ // Allow loading css through js
+ //
+ // Reference: https://github.com/postcss/postcss-loader
+ // Postprocess your css with PostCSS plugins
+ test: /\.css$/,
+ loader: !TEST
+ // Reference: https://github.com/webpack/extract-text-webpack-plugin
+ // Extract css files in production builds
+ //
+ // Reference: https://github.com/webpack/style-loader
+ // Use style-loader in development for hot-loading
+ ? ExtractTextPlugin.extract('style', 'css?sourceMap!postcss')
+ // Reference: https://github.com/webpack/null-loader
+ // Skip loading css in test mode
+ : 'null'
+ }<% if(!filters.css) { %>, {
+ <%_ if(filters.sass) { _%>
+ // SASS LOADER
+ // Reference: https://github.com/jtangelder/sass-loader
+ test: /\.(scss|sass)$/,
+ loaders: ['style', 'css', 'sass'],
+ include: [
+ path.resolve(__dirname, 'node_modules/bootstrap-sass/assets/stylesheets/.scss'),
+ path.resolve(__dirname, 'client/app/app.scss')
+ ]<% } %>
+ <%_ if(filters.less) { _%>
+ // LESS LOADER
+ // Reference: https://github.com/
+ test: /\.less$/,
+ loaders: ['style', 'css', 'less'],
+ include: [
+ path.resolve(__dirname, 'node_modules/'),//TODO: bootstrap
+ path.resolve(__dirname, 'client/app/app.less')
+ ]<% } %>
+ <%_ if(filters.stylus) { _%>
+ // Stylus LOADER
+ // Reference: https://github.com/
+ test: /\.styl$/,
+ loaders: ['style', 'css', 'stylus'],
+ include: [
+ path.resolve(__dirname, 'node_modules/'),//TODO: bootstrap
+ path.resolve(__dirname, 'client/app/app.styl')
+ ]<% } %>
+ }<% } %>]
+ };
+
+ config.module.postLoaders = [{
+ test: /\.<%= scriptExt %>$/,
+ loader: 'ng-annotate?single_quotes'
+ }];
+
+ <%_ if(filters.babel) { _%>
+ // ISPARTA INSTRUMENTER LOADER
+ // Reference: https://github.com/ColCh/isparta-instrumenter-loader
+ // Instrument JS files with Isparta for subsequent code coverage reporting
+ // Skips node_modules and spec files
+ if(TEST) {
+ config.module.preLoaders.push({
+ //delays coverage til after tests are run, fixing transpiled source coverage error
+ test: /\.js$/,
+ exclude: /(node_modules|spec\.js|mock\.js)/,
+ loader: 'isparta-instrumenter',
+ query: {
+ babel: {
+ // optional: ['runtime', 'es7.classProperties', 'es7.decorators']
+ }
+ }
+ });
+ }<% } %>
+ <%_ if(filters.ts) { _%>
+ //TODO: TS Instrumenter<% } %>
+
+ /**
+ * PostCSS
+ * Reference: https://github.com/postcss/autoprefixer-core
+ * Add vendor prefixes to your css
+ */
+ config.postcss = [
+ autoprefixer({
+ browsers: ['last 2 version']
+ })
+ ];
+
+ /**
+ * Plugins
+ * Reference: http://webpack.github.io/docs/configuration.html#plugins
+ * List: http://webpack.github.io/docs/list-of-plugins.html
+ */
+ config.plugins = [
+ /*
+ * Plugin: ForkCheckerPlugin
+ * Description: Do type checking in a separate process, so webpack don't need to wait.
+ *
+ * See: https://github.com/s-panferov/awesome-typescript-loader#forkchecker-boolean-defaultfalse
+ */
+ new ForkCheckerPlugin(),
+
+ // Reference: https://github.com/webpack/extract-text-webpack-plugin
+ // Extract css files
+ // Disabled when in test mode or not in build mode
+ new ExtractTextPlugin('[name].[hash].css', {
+ disable: !BUILD || TEST
+ })
+ ];
+
+ if(!TEST) {
+ config.plugins.push(new CommonsChunkPlugin({
+ name: 'vendor',
+
+ // filename: "vendor.js"
+ // (Give the chunk a different name)
+
+ minChunks: Infinity
+ // (with more entries, this ensures that no other module
+ // goes into the vendor chunk)
+ }));
+ }
+
+ // Skip rendering index.html in test mode
+ // Reference: https://github.com/ampedandwired/html-webpack-plugin
+ // Render index.html
+ let htmlConfig = {
+ template: 'client/_index.html'
+ }
+ if(E2E) {
+ htmlConfig.filename = '../client/index.html';
+ htmlConfig.alwaysWriteToDisk = true;
+ }
+ config.plugins.push(new HtmlWebpackPlugin(htmlConfig));
+ if(E2E) config.plugins.push(new HtmlWebpackHarddiskPlugin());
+
+ // Add build specific plugins
+ if(BUILD) {
+ config.plugins.push(
+ // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
+ // Only emit files when there are no errors
+ new webpack.NoErrorsPlugin(),
+
+ // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin
+ // Dedupe modules in the output
+ new webpack.optimize.DedupePlugin(),
+
+ // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
+ // Minify all javascript, switch loaders to minimizing mode
+ new webpack.optimize.UglifyJsPlugin({
+ mangle: false,
+ output: {
+ comments: false
+ },
+ compress: {
+ warnings: false
+ }
+ }),
+
+ // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
+ // Define free global variables
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: '"production"'
+ }
+ })
+ );
+ }
+
+ if(DEV) {
+ config.plugins.push(
+ // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
+ // Define free global variables
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: '"development"'
+ }
+ })
+ );
+ }
+
+ config.cache = DEV;
+
+ if(TEST) {
+ config.stats = {
+ colors: true,
+ reasons: true
+ };
+ config.debug = false;
+ }
+
+ /**
+ * Dev server configuration
+ * Reference: http://webpack.github.io/docs/configuration.html#devserver
+ * Reference: http://webpack.github.io/docs/webpack-dev-server.html
+ */
+ config.devServer = {
+ contentBase: './client/',
+ stats: {
+ modules: false,
+ cached: false,
+ colors: true,
+ chunk: false
+ }
+ };
+
+ config.node = {
+ global: 'window',
+ process: true,
+ crypto: 'empty',
+ clearImmediate: false,
+ setImmediate: false
+ };
+
+ return config;
+};
diff --git a/templates/app/webpack.test.js b/templates/app/webpack.test.js
new file mode 100644
index 000000000..9175a1b69
--- /dev/null
+++ b/templates/app/webpack.test.js
@@ -0,0 +1,8 @@
+/**
+ * Webpack config for tests
+ */
+module.exports = require('./webpack.make')({
+ BUILD: false,
+ TEST: true,
+ DEV: false
+});
diff --git a/test/fixtures/.bowerrc b/test/fixtures/.bowerrc
deleted file mode 100644
index 69fad3580..000000000
--- a/test/fixtures/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "bower_components"
-}
diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json
index 3f652f692..b1c868ce5 100644
--- a/test/fixtures/.yo-rc.json
+++ b/test/fixtures/.yo-rc.json
@@ -26,7 +26,7 @@
"models": true,
"mongooseModels": true,
"mongoose": true,
- "grunt": true,
+ "gulp": true,
"mocha": true,
"jasmine": false,
"expect": true