diff --git a/.travis.yml b/.travis.yml
index 4e2b9e3f8..eec3dc145 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ before_install:
- ./scripts/sauce_connect_setup.sh
- gem update --system
- gem install sass --version "=3.3.7"
- - npm install -g bower grunt-cli
+ - npm install -g gulp-cli
services: mongodb
cache:
directories:
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 9ffab02d5..6cf5aa437 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -14,7 +14,6 @@ Transpiler | Babel / TypeScript
Markup | HTML / Jade
CSS | CSS / LESS / SCSS / Stylus
Router | ngRoute / ui-router
-Build Tool | Grunt / Gulp
Client Tests | Jasmine / Mocha
DB | MongoDB / SQL
Auth | Y / N
diff --git a/circle.yml b/circle.yml
index 4c20b34f2..ad8bf69e5 100644
--- a/circle.yml
+++ b/circle.yml
@@ -3,7 +3,7 @@ machine:
node:
version: 5.11.1
post:
- - npm install -g gulp-cli grunt-cli bower
+ - npm install -g gulp-cli
## Customize checkout
checkout:
diff --git a/docs/generators/heroku.md b/docs/generators/heroku.md
index 3d62c7c5d..20336b82c 100644
--- a/docs/generators/heroku.md
+++ b/docs/generators/heroku.md
@@ -41,14 +41,12 @@ Your app should now be live. To view it run `heroku open`.
> heroku config:set DOMAIN=
>
-> WARNING: Deployment is untested with Gulp
-
To make your deployment process easier consider using [grunt-build-control](https://github.com/robwierzbowski/grunt-build-control).
#### Pushing Updates
- grunt
+ gulp build
Commit and push the resulting build, located in your dist folder:
- grunt buildcontrol:heroku
+ gulp buildcontrol:heroku
diff --git a/docs/generators/openshift.md b/docs/generators/openshift.md
index 0d2ed8970..6577eab83 100644
--- a/docs/generators/openshift.md
+++ b/docs/generators/openshift.md
@@ -31,8 +31,8 @@ To make your deployment process easier consider using [grunt-build-control](http
**Pushing Updates**
- grunt
+ gulp build
Commit and push the resulting build, located in your dist folder:
- grunt buildcontrol:openshift
+ gulp buildcontrol:openshift
diff --git a/docs/index.md b/docs/index.md
index f25320da3..043388816 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -50,7 +50,7 @@ All of these can be updated with `bower update` as new versions are released.
## Injection
-A grunt/gulp task looks for new files in your `client/app` and `client/components` folder and automatically injects them in the appropriate places based on an injection block.
+A gulp task looks for new files in your `client/app` and `client/components` folder and automatically injects them in the appropriate places based on an injection block.
* `less` files into `client/app/app.less`
* `scss` files into `client/app/app.scss`
@@ -68,11 +68,11 @@ A `.yo-rc` file is generated for helping you copy configuration across projects,
## Testing
-Running `grunt test` will run the client and server unit tests with karma and mocha.
+Running `gulp test` will run the client and server unit tests with karma and mocha.
-Use `grunt test:server` to only run server tests.
+Use `gulp test:server` to only run server tests.
-Use `grunt test:client` to only run client tests.
+Use `gulp test:client` to only run client tests.
**Protractor tests**
@@ -80,11 +80,11 @@ To setup protractor e2e tests, you must first run
`npm run update-webdriver`
-Use `grunt test:e2e` to have protractor go through tests located in the `e2e` folder.
+Use `gulp test:e2e` to have protractor go through tests located in the `e2e` folder.
**Code Coverage**
-Use `grunt test:coverage` to run mocha-istanbul and generate code coverage reports.
+Use `gulp test:coverage` to run mocha-istanbul and generate code coverage reports.
`coverage/server` will be populated with `e2e` and `unit` folders containing the `lcov` reports.
@@ -97,11 +97,12 @@ The coverage taget has 3 available options:
**Debugging**
-Use `grunt serve:debug` for a more debugging-friendly environment.
+Use `gulp serve:debug` for a more debugging-friendly environment.
## Environment Variables
-Keeping your app secrets and other sensitive information in source control isn't a good idea. To have grunt launch your app with specific environment variables, add them to the git ignored environment config file: `server/config/local.env.js`.
+Keeping your app secrets and other sensitive information in source control isn't a good idea.
+To have gulp launch your app with specific environment variables, add them to the git ignored environment config file: `server/config/local.env.js`.
## Project Structure
diff --git a/gulpfile.js b/gulpfile.js
index 647f84404..6d6752aeb 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -35,7 +35,7 @@ const transpile = lazypipe()
.pipe(babel);
gulp.task('clean', () => {
- return del(['generators/**/*', './test/(**|!fixtures/node_modules|!fixtures/bower_components)/*']);
+ return del(['generators/**/*', './test/(**|!fixtures/node_modules)/*']);
});
gulp.task('babel', () => {
@@ -86,7 +86,7 @@ var processJson = function(src, dest, opt) {
if(/package.json/g.test(src) && opt.test) {
delete json.scripts.postinstall;
- json.scripts['update-webdriver'] = 'node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update || node node_modules/protractor/bin/webdriver-manager update';
+ json.scripts['update-webdriver'] = 'node node_modules/gulp-protractor-runner/node_modules/protractor/bin/webdriver-manager update || node node_modules/protractor/bin/webdriver-manager update';
}
// set properties
@@ -113,10 +113,7 @@ function updateFixtures(target) {
const dest = __dirname + (deps ? '/angular-fullstack-deps/' : '/test/fixtures/');
const appName = deps ? 'angular-fullstack-deps' : 'tempApp';
- return Promise.all([
- processJson('templates/app/_package.json', dest + 'package.json', {appName, genVer, private: !deps, test: test}),
- processJson('templates/app/_bower.json', dest + 'bower.json', {appName, genVer, private: !deps, test: test})
- ]);
+ return processJson('templates/app/_package.json', dest + 'package.json', {appName, genVer, private: !deps, test: test});
}
gulp.task('updateFixtures', cb => {
@@ -143,16 +140,13 @@ function execAsync(cmd, opt) {
}
gulp.task('installFixtures', function() {
- gutil.log('installing npm & bower dependencies for generated app');
+ gutil.log('installing npm dependencies for generated app');
let progress = setInterval(() => {
process.stdout.write('.');
}, 1 * 1000);
shell.cd('test/fixtures');
- return Promise.all([
- execAsync('npm install --quiet', {cwd: '../fixtures'}),
- execAsync('bower install', {cwd: '../fixtures'})
- ]).then(() => {
+ execAsync('npm install --quiet', {cwd: '../fixtures'}).then(() => {
process.stdout.write('\n');
if(!process.env.SAUCE_USERNAME) {
gutil.log('running npm run-script update-webdriver');
diff --git a/package.json b/package.json
index 613adbeb4..910b44953 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "generator-angular-fullstack",
- "version": "3.7.6",
+ "version": "4.0.0-rc.0",
"description": "Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node",
"keywords": [
"yeoman-generator",
@@ -40,11 +40,12 @@
"babel-plugin-transform-flow-strip-types": "^6.7.0",
"bluebird": "^3.3.5",
"chalk": "^1.1.0",
- "generator-ng-component": "~0.3.1",
+ "generator-ng-component": "~1.0.1",
"glob": "^7.0.3",
"gulp-babel": "^6.1.2",
"gulp-beautify": "^2.0.0",
"gulp-filter": "^4.0.0",
+ "gulp-tap": "^0.1.3",
"insight": "~0.8.1",
"lodash": "^4.13.1",
"semver": "^5.1.0",
@@ -73,7 +74,6 @@
"gulp-plumber": "^1.1.0",
"gulp-util": "^3.0.7",
"jit-grunt": "~0.10.0",
- "jscs": "^3.0.3",
"lazypipe": "^1.0.1",
"merge-stream": "^1.0.0",
"minimatch": "^3.0.2",
@@ -87,8 +87,8 @@
"yeoman-test": "~1.4.0"
},
"engines": {
- "node": "^5.10.1",
- "npm": "^3.8.3"
+ "node": "^6.2.2",
+ "npm": "^3.9.5"
},
"license": "BSD-2-Clause"
}
diff --git a/readme.md b/readme.md
index d6dc4e64a..dfaf24c7b 100644
--- a/readme.md
+++ b/readme.md
@@ -19,9 +19,9 @@
## Usage
-Install `yo`, `grunt-cli`/`gulp-cli`, `bower`, and `generator-angular-fullstack`:
+Install `yo`, `gulp-cli`, and `generator-angular-fullstack`:
```
-npm install -g yo grunt-cli gulp-cli bower generator-angular-fullstack
+npm install -g yo gulp-cli generator-angular-fullstack
```
> To install the latest 4.0.0 Beta with Webpack support, use `generator-angular-fullstack@beta`
@@ -36,7 +36,7 @@ Run `yo angular-fullstack`, optionally passing an app name:
yo angular-fullstack [app-name]
```
-Run `grunt`/`gulp build` for building, `grunt serve`/`gulp serve` for development, and `grunt serve:dist`/`gulp serve:dist` for a preview of the built app.
+Run `gulp build` for building, `gulp serve` for development, and `gulp serve:dist` for a preview of the built app.
## Prerequisites
@@ -47,7 +47,7 @@ Run `grunt`/`gulp build` for building, `grunt serve`/`gulp serve` for developmen
**General**
-* Build Systems: `Grunt` (deprecated, will be removed in 4.0.0), `Gulp`
+* Build Systems: `Gulp`
* Testing:
* `Jasmine`
* `Mocha + Chai + Sinon`
diff --git a/src/generators/app/index.js b/src/generators/app/index.js
index a6d2c5543..3e199b59a 100644
--- a/src/generators/app/index.js
+++ b/src/generators/app/index.js
@@ -11,6 +11,7 @@ import insight from '../insight-init';
import {exec} from 'child_process';
import babelStream from 'gulp-babel';
import beaufityStream from 'gulp-beautify';
+import tap from 'gulp-tap';
import filter from 'gulp-filter';
import semver from 'semver';
@@ -26,6 +27,12 @@ export class Generator extends Base {
defaults: false
});
+ this.option('skip-config', {
+ desc: 'Always use existing .yo-rc.json',
+ type: Boolean,
+ defaults: false
+ });
+
this.option('app-suffix', {
desc: 'Allow a custom suffix to be added to the module name',
type: String,
@@ -93,34 +100,38 @@ export class Generator extends Base {
checkForConfig: function() {
var existingFilters = this.config.get('filters');
- if(existingFilters) {
- return this.prompt([{
- type: 'confirm',
- name: 'skipConfig',
- message: 'Existing .yo-rc configuration found, would you like to use it?',
- default: true,
- }]).then(answers => {
- this.skipConfig = answers.skipConfig;
-
- if(this.skipConfig) {
- insight.track('skipConfig', 'true');
- this.filters = existingFilters;
-
- this.scriptExt = this.filters.ts ? 'ts' : 'js';
- this.templateExt = this.filters.jade ? 'jade' : 'html';
- this.styleExt = this.filters.sass ? 'scss' :
- this.filters.less ? 'less' :
- this.filters.stylus ? 'styl' :
- 'css';
- } else {
- insight.track('skipConfig', 'false');
- this.filters = {};
- this.forceConfig = true;
- this.config.set('filters', this.filters);
- this.config.forceSave();
- }
- });
- }
+ if(!existingFilters) return;
+
+ let promise = this.options['skip-config']
+ ? Promise.resolve({skipConfig: true})
+ : this.prompt([{
+ type: 'confirm',
+ name: 'skipConfig',
+ message: 'Existing .yo-rc configuration found, would you like to use it?',
+ default: true,
+ }]);
+
+ return promise.then(answers => {
+ this.skipConfig = answers.skipConfig;
+
+ if(this.skipConfig) {
+ insight.track('skipConfig', 'true');
+ this.filters = existingFilters;
+
+ this.scriptExt = this.filters.ts ? 'ts' : 'js';
+ this.templateExt = this.filters.jade ? 'jade' : 'html';
+ this.styleExt = this.filters.sass ? 'scss' :
+ this.filters.less ? 'less' :
+ this.filters.stylus ? 'styl' :
+ 'css';
+ } else {
+ insight.track('skipConfig', 'false');
+ this.filters = {};
+ this.forceConfig = true;
+ this.config.set('filters', this.filters);
+ this.config.forceSave();
+ }
+ });
},
assignPorts() {
this.devPort = this.options['dev-port'];
@@ -149,12 +160,12 @@ export class Generator extends Base {
}[val];
}
}, {
- // TODO: enable once Babel setup supports Flow
- // type: 'confirm',
- // name: 'flow',
- // message: 'Would you like to use Flow types with Babel?',
- // when: answers => answers.transpiler === 'babel'
- // }, {
+ type: 'confirm',
+ name: 'flow',
+ default: false,
+ message: 'Would you like to use Flow types with Babel?',
+ when: answers => answers.transpiler === 'babel'
+ }, {
type: 'list',
name: 'markup',
message: 'What would you like to write markup with?',
@@ -317,16 +328,6 @@ export class Generator extends Base {
this.log('\n# Project\n');
return this.prompt([{
- type: 'list',
- name: 'buildtool',
- message: 'Would you like to use Gulp or Grunt?',
- choices: ['Gulp', 'Grunt (deprecated)'],
- default: 0,
- filter: val => ({
- 'Gulp': 'gulp',
- 'Grunt (deprecated)': 'grunt'
- })[val]
- }, {
type: 'list',
name: 'testing',
message: 'What would you like to write tests with?',
@@ -346,9 +347,6 @@ export class Generator extends Base {
filter: val => val.toLowerCase(),
when: answers => answers.testing === 'mocha'
}]).then(answers => {
- this.filters[answers.buildtool] = true;
- insight.track('buildtool', answers.buildtool);
-
this.filters[answers.testing] = true;
insight.track('testing', answers.testing);
if(answers.testing === 'mocha') {
@@ -417,6 +415,7 @@ export class Generator extends Base {
if(this.filters.less) extensions.push('less');
filters.push('es6'); // Generate ES6 syntax code
+ filters.push('webpack'); // Generate ES6 Module imports/exports
this.composeWith('ng-component', {
options: {
@@ -430,7 +429,7 @@ export class Generator extends Base {
'basePath': 'client',
'forceConfig': this.forceConfig
}
- }, { local: require.resolve('generator-ng-component/app/index.js') });
+ }, { local: require.resolve('generator-ng-component/generators/app/index.js') });
},
ngModules: function() {
var angModules = [
@@ -475,24 +474,32 @@ export class Generator extends Base {
]);
*/
+ const flow = this.filters.flow;
+
let babelPlugins = [
'babel-plugin-syntax-flow',
'babel-plugin-syntax-class-properties'
];
- // TODO: enable once Babel setup supports Flow
- // if(this.filters.babel && !this.filters.flow) {
+ if(this.filters.babel && !flow) {
babelPlugins.push('babel-plugin-transform-flow-strip-types');
- // }
+ }
- const jsFilter = filter(['client/**/*.js'], {restore: true});
+ let jsFilter = filter(['client/**/*.js'], {restore: true});
this.registerTransformStream([
jsFilter,
babelStream({
plugins: babelPlugins.map(require.resolve),
/* Babel get's confused about these if you're using an `npm link`ed
generator-angular-fullstack, thus the `require.resolve` */
- // retainLines: true,
+ shouldPrintComment(commentContents) {
+ if(flow) {
+ return true;
+ } else {
+ // strip `// @flow` comments if not using flow
+ return !(/@flow/.test(commentContents));
+ }
+ },
babelrc: false // don't grab the generator's `.babelrc`
}),
beaufityStream({
@@ -519,6 +526,43 @@ export class Generator extends Base {
jsFilter.restore
]);
+ /**
+ * TypeScript doesn't play nicely with things that don't have a default export
+ */
+ if(this.filters.ts) {
+ const modulesToFix = [
+ ['angular', 'angular'],
+ ['ngCookies', 'angular-cookies'],
+ ['ngResource', 'angular-resource'],
+ ['ngSanitize', 'angular-sanitize'],
+ ['uiRouter', 'angular-ui-router'],
+ ['ngRoute', 'angular-route'],
+ ['uiBootstrap', 'angular-ui-bootstrap'],
+ ['ngMessages', 'angular-messages'],
+ ['io', 'socket.io-client']
+ ];
+ function replacer(contents) {
+ modulesToFix.forEach(([moduleName, importName]) => {
+ contents = contents.replace(
+ `import ${moduleName} from '${importName}'`,
+ `const ${moduleName} = require('${importName}')`
+ );
+ });
+ return contents;
+ }
+
+ let tsFilter = filter(['client/**/*.ts'], {restore: true});
+ this.registerTransformStream([
+ tsFilter,
+ tap(function(file, t) {
+ var contents = file.contents.toString();
+ contents = replacer(contents);
+ file.contents = new Buffer(contents);
+ }),
+ tsFilter.restore
+ ]);
+ }
+
let self = this;
this.sourceRoot(path.join(__dirname, '../../templates/app'));
this.processDirectory('.', '.');
@@ -541,14 +585,10 @@ export class Generator extends Base {
};
}
- get install() {
- return {
- installDeps: function() {
- this.installDependencies({
- skipInstall: this.options['skip-install']
- });
- }
- };
+ install() {
+ if(!this.options['skip-install']) {
+ this.spawnCommand('npm', ['install']);
+ }
}
get end() {
diff --git a/src/generators/component/index.js b/src/generators/component/index.js
index 9d742dca4..d27d3430c 100644
--- a/src/generators/component/index.js
+++ b/src/generators/component/index.js
@@ -6,7 +6,7 @@ class Generator extends Base {
this.composeWith('ng-component:component', {
arguments: this.arguments
}, {
- local: require.resolve('generator-ng-component/component')
+ local: require.resolve('generator-ng-component/generators/component')
});
}
}
diff --git a/src/generators/controller/index.js b/src/generators/controller/index.js
index 0c9a53d87..8504e5f2b 100644
--- a/src/generators/controller/index.js
+++ b/src/generators/controller/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:controller', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/controller') });
+ this.composeWith('ng-component:controller', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/controller') });
}
});
diff --git a/src/generators/decorator/index.js b/src/generators/decorator/index.js
index 578427ae2..070232031 100644
--- a/src/generators/decorator/index.js
+++ b/src/generators/decorator/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:decorator', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/decorator') });
+ this.composeWith('ng-component:decorator', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/decorator') });
}
});
diff --git a/src/generators/directive/index.js b/src/generators/directive/index.js
index 5ac3c7c0e..3213835ec 100644
--- a/src/generators/directive/index.js
+++ b/src/generators/directive/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:directive', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/directive') });
+ this.composeWith('ng-component:directive', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/directive') });
}
});
diff --git a/src/generators/endpoint/index.js b/src/generators/endpoint/index.js
index 144325c2c..04fcd2be5 100644
--- a/src/generators/endpoint/index.js
+++ b/src/generators/endpoint/index.js
@@ -5,7 +5,6 @@ import {Base} from 'yeoman-generator';
import {genNamedBase} from '../generator-base';
export class Generator extends Base {
-
constructor(...args) {
super(...args);
@@ -33,33 +32,31 @@ export class Generator extends Base {
}
prompting() {
- var promptCb = (props) => {
+ let promptCb = props => {
if(props.route.charAt(0) !== '/') {
- props.route = '/' + props.route;
+ props.route = `/${props.route}`;
}
this.route = props.route;
- if (props.models) {
+ if(props.models) {
delete this.filters.mongoose;
delete this.filters.mongooseModels;
delete this.filters.sequelize;
delete this.filters.sequelizeModels;
this.filters[props.models] = true;
- this.filters[props.models + 'Models'] = true;
+ this.filters[`${props.models}Models`] = true;
}
};
- if (this.options.route) {
- if (this.filters.mongoose && this.filters.sequelize) {
- if (this.options.models) {
- return promptCb(this.options);
- }
+ if(this.options.route) {
+ if(this.filters.mongoose && this.filters.sequelize && this.options.models) {
+ return promptCb(this.options);
} else {
- if (this.filters.mongooseModels) { this.options.models = 'mongoose'; }
- else if (this.filters.sequelizeModels) { this.options.models = 'sequelize'; }
- else { delete this.options.models; }
+ if(this.filters.mongooseModels) this.options.models = 'mongoose';
+ else if(this.filters.sequelizeModels) this.options.models = 'sequelize';
+ else delete this.options.models;
return promptCb(this.options);
}
}
@@ -67,26 +64,26 @@ export class Generator extends Base {
var name = this.name;
var base = this.config.get('routesBase') || '/api/';
- if(base.charAt(base.length-1) !== '/') {
- base = base + '/';
+ if(base.charAt(base.length - 1) !== '/') {
+ base = `${base}/`;
}
// pluralization defaults to true for backwards compat
- if (this.config.get('pluralizeRoutes') !== false) {
- name = name + 's';
+ if(this.config.get('pluralizeRoutes') !== false) {
+ name = `${name}s`;
}
var prompts = [{
name: 'route',
message: 'What will the url of your endpoint be?',
- default: base + name
+ default: `${base}${name}`
}, {
type: 'list',
name: 'models',
message: 'What would you like to use for the endpoint\'s models?',
- choices: [ 'Mongoose', 'Sequelize' ],
+ choices: ['Mongoose', 'Sequelize'],
default: this.filters.sequelizeModels ? 1 : 0,
- filter: (val) => val.toLowerCase(),
+ filter: val => val.toLowerCase(),
when: () => this.filters.mongoose && this.filters.sequelize
}];
@@ -94,8 +91,9 @@ export class Generator extends Base {
}
configuring() {
- this.routeDest = path.join(this.options.endpointDirectory ||
- this.config.get('endpointDirectory') || 'server/api/', this.name);
+ this.routeDest = path.join(this.options.endpointDirectory
+ || this.config.get('endpointDirectory')
+ || 'server/api/', this.name);
}
writing() {
@@ -111,13 +109,13 @@ export class Generator extends Base {
file: routesFile,
needle: this.config.get('routesNeedle'),
splicable: [
- "app.use(\'" + this.route +"\', require(\'" + reqPath + "\'));"
+ `app.use('${this.route}', require('${reqPath}'));`
]
};
this.rewriteFile(routeConfig);
}
- if (this.filters.socketio && this.config.get('insertSockets')) {
+ if(this.filters.socketio && this.config.get('insertSockets')) {
var socketsFile = this.config.get('registerSocketsFile');
var reqPath = this.relativeRequire(this.routeDest + '/' + this.basename +
'.socket', socketsFile);
@@ -125,20 +123,20 @@ export class Generator extends Base {
file: socketsFile,
needle: this.config.get('socketsNeedle'),
splicable: [
- "require(\'" + reqPath + "\').register(socket);"
+ `require('${reqPath}').register(socket);`
]
};
this.rewriteFile(socketConfig);
}
- if (this.filters.sequelize && this.config.get('insertModels')) {
+ if(this.filters.sequelize && this.config.get('insertModels')) {
var modelsFile = this.config.get('registerModelsFile');
- var reqPath = this.relativeRequire(this.routeDest + '/' + this.basename + '.model', modelsFile);
+ var reqPath = this.relativeRequire(`${this.routeDest}/${this.basename}.model`, modelsFile);
var modelConfig = {
file: modelsFile,
needle: this.config.get('modelsNeedle'),
splicable: [
- "db." + this.classedName + " = db.sequelize.import(\'" + reqPath +"\');"
+ `db.${this.classedName} = db.sequelize.import('${reqPath}');`
]
};
this.rewriteFile(modelConfig);
diff --git a/src/generators/filter/index.js b/src/generators/filter/index.js
index badbe02a1..0226dc8cf 100644
--- a/src/generators/filter/index.js
+++ b/src/generators/filter/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:filter', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/filter') });
+ this.composeWith('ng-component:filter', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/filter') });
}
});
diff --git a/src/generators/heroku/index.js b/src/generators/heroku/index.js
index e728d2b66..0f86008f8 100644
--- a/src/generators/heroku/index.js
+++ b/src/generators/heroku/index.js
@@ -110,11 +110,10 @@ export default class Generator extends Base {
build() {
if(this.abort) return;
var done = this.async();
- var buildCommand = this.filters.grunt ? 'grunt build' : 'gulp build';
this.log(chalk.bold('\nBuilding dist folder, please wait...'));
- var child = exec(buildCommand, (err, stdout) => {
+ var child = exec('gulp build', (err, stdout) => {
done();
});
@@ -192,9 +191,9 @@ Your app should now be live. To view it run
this.log(chalk.yellow(`
After app modification run
-\t${chalk.bold(this.filters.grunt ? 'grunt build' : 'gulp build')}
+\t${chalk.bold('gulp build')}
Then deploy with
-\t${chalk.bold(this.filters.grunt ? 'grunt buildcontrol:heroku' : 'gulp buildcontrol:heroku')}`));
+\t${chalk.bold('gulp buildcontrol:heroku')}`));
}
done();
});
diff --git a/src/generators/openshift/index.js b/src/generators/openshift/index.js
index 3032198b3..f4c5035cd 100644
--- a/src/generators/openshift/index.js
+++ b/src/generators/openshift/index.js
@@ -196,10 +196,9 @@ Generator.prototype.enableOpenShiftHotDeploy = function enableOpenshiftHotDeploy
Generator.prototype.build = function build() {
if(this.abort || !this.openshift_remote_exists ) return;
var done = this.async();
- var buildCommand = this.filters.grunt ? 'grunt build' : 'gulp build';
this.log(chalk.bold('\nBuilding dist folder, please wait...'));
- var child = exec(buildCommand, function (err, stdout) {
+ var child = exec('gulp build', function (err, stdout) {
if (err) {
this.log.error(err);
}
@@ -297,10 +296,10 @@ Generator.prototype.restartApp = function restartApp() {
}
this.log(chalk.yellow(
- 'After app modification run\n\t' +
- chalk.bold(this.filters.grunt ? 'grunt build' : 'gulp build') +
+ 'After app modification run\n\t' +
+ chalk.bold('gulp build') +
'\nThen deploy with\n\t' +
- chalk.bold(this.filters.grunt ? 'grunt buildcontrol:openshift' : 'gulp buildcontrol:openshift')
+ chalk.bold('gulp buildcontrol:openshift')
));
}.bind(this));
};
diff --git a/src/generators/provider/index.js b/src/generators/provider/index.js
index 8e0213799..1410cfe4b 100644
--- a/src/generators/provider/index.js
+++ b/src/generators/provider/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:provider', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/provider') });
+ this.composeWith('ng-component:provider', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/provider') });
}
});
diff --git a/src/generators/route/index.js b/src/generators/route/index.js
index 9f6f1f12d..bfe6e4c26 100644
--- a/src/generators/route/index.js
+++ b/src/generators/route/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:route', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/route') });
+ this.composeWith('ng-component:route', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/route') });
}
});
diff --git a/src/generators/service/index.js b/src/generators/service/index.js
index 7ef1e214a..93f9fc266 100644
--- a/src/generators/service/index.js
+++ b/src/generators/service/index.js
@@ -3,7 +3,7 @@ var yeoman = require('yeoman-generator');
var Generator = yeoman.Base.extend({
compose: function() {
- this.composeWith('ng-component:service', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/service') });
+ this.composeWith('ng-component:service', {arguments: this.arguments}, { local: require.resolve('generator-ng-component/generators/service') });
}
});
diff --git a/src/test/endpoint.test.js b/src/test/endpoint.test.js
index 729535b9f..5101bfd73 100644
--- a/src/test/endpoint.test.js
+++ b/src/test/endpoint.test.js
@@ -6,9 +6,6 @@ import Promise from 'bluebird';
import helpers from 'yeoman-test';
import assert from 'yeoman-assert';
import minimatch from 'minimatch';
-import Checker from 'jscs';
-const jscs = new Checker();
-jscs.registerDefaultRules();
import * as getExpectedFiles from './get-expected-files';
import {
copyAsync,
@@ -21,7 +18,6 @@ import {
const TEST_DIR = __dirname;
const defaultOptions = {
- buildtool: 'grunt',
script: 'js',
transpiler: 'babel',
markup: 'html',
@@ -53,12 +49,8 @@ function runEndpointGen(name, opt={}) {
dir = _dir;
// symlink our dependency directories
- return Promise.all([
- fs.mkdirAsync(dir + '/client').then(() => {
- return fs.symlinkAsync(__dirname + '/fixtures/bower_components', dir + '/client/bower_components');
- }),
- fs.symlinkAsync(__dirname + '/fixtures/node_modules', dir + '/node_modules')
- ]).then(done);
+ return fs.symlinkAsync(__dirname + '/fixtures/node_modules', dir + '/node_modules')
+ .then(done);
})
.withOptions(options)
.withArguments([name])
@@ -75,7 +67,7 @@ function runEndpointGen(name, opt={}) {
});
}
-let jshintCmd = path.join(TEST_DIR, '/fixtures/node_modules/.bin/jshint');
+let eslintCmd = path.join(TEST_DIR, '/fixtures/node_modules/.bin/eslint');
function testFile(command, _path) {
_path = path.normalize(_path);
return fs.accessAsync(_path, fs.R_OK).then(() => {
@@ -83,42 +75,20 @@ function testFile(command, _path) {
});
}
-function jshintDir(dir, name, folder) {
+function eslintDir(dir, name, folder) {
if(!folder) folder = name;
let endpointDir = path.join(dir, 'server/api', folder);
let regFiles = fs.readdirAsync(endpointDir)
.then(files => files.filter(file => minimatch(file, '**/!(*.spec|*.mock|*.integration).js', {dot: true})))
- .map(file => testFile(jshintCmd, path.join('./server/api/', folder, file)));
+ .map(file => testFile(eslintCmd, path.join('./server/api/', folder, file)));
let specFiles = fs.readdirAsync(endpointDir)
.then(files => files.filter(file => minimatch(file, '**/+(*.spec|*.mock|*.integration).js', {dot: true})))
- .map(file => testFile(`${jshintCmd} --config server/.jshintrc-spec`, path.join('./server/api/', folder, file)));
+ .map(file => testFile(`${eslintCmd} --env node,es6,mocha --global sinon,expect`, path.join('./server/api/', folder, file)));
return Promise.all([regFiles, specFiles]);
}
-function jscsDir(dir, name, folder) {
- if(!folder) folder = name;
- let endpointDir = path.join(dir, 'server/api', folder);
-
- return fs.readdirAsync(endpointDir).then(files => {
- return Promise.map(files, file => {
- return fs.readFileAsync(path.join('server/api', folder, file), 'utf8').then(data => {
- let results = jscs.checkString(data)
- let errors = results.getErrorList();
- if(errors.length === 0) {
- return Promise.resolve();
- } else {
- errors.forEach(error => {
- var colorizeOutput = true;
- console.log(results.explainError(error, colorizeOutput) + '\n');
- });
- return Promise.reject();
- }
- });
- });
- });
-}
var config;
var genDir;
@@ -128,10 +98,6 @@ describe('angular-fullstack:endpoint', function() {
return Promise.all([
runGen(defaultOptions).then(_dir => {
genDir = _dir;
-
- return fs.readFileAsync(path.join(genDir, '.jscsrc'), 'utf8').then(data => {
- jscs.configure(JSON.parse(data));
- });
}),
readJSON(path.join(TEST_DIR, 'fixtures/.yo-rc.json')).then(_config => {
_config['generator-angular-fullstack'].insertRoutes = false;
@@ -150,9 +116,8 @@ describe('angular-fullstack:endpoint', function() {
dir = _dir;
return Promise.all([
- copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'),
- copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'),
- copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc')
+ copyAsync(path.join(genDir, '/.eslintrc'), './.eslintrc'),
+ copyAsync(path.join(genDir, '/server/.eslintrc'), './server/.eslintrc')
]);
});
});
@@ -161,12 +126,8 @@ describe('angular-fullstack:endpoint', function() {
assert.file(getExpectedFiles.endpoint('foo'));
});
- it('should pass jscs', function() {
- return jscsDir(dir, 'foo').should.be.fulfilled();
- });
-
it('should pass lint', function() {
- return jshintDir(dir, 'foo').should.be.fulfilled();
+ return eslintDir(dir, 'foo').should.be.fulfilled();
});
});
@@ -177,9 +138,8 @@ describe('angular-fullstack:endpoint', function() {
dir = _dir;
return Promise.all([
- copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'),
- copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'),
- copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc')
+ copyAsync(path.join(genDir, '/.eslintrc'), './.eslintrc'),
+ copyAsync(path.join(genDir, '/server/.eslintrc'), './server/.eslintrc')
]);
});
});
@@ -188,12 +148,8 @@ describe('angular-fullstack:endpoint', function() {
assert.file(getExpectedFiles.endpoint('Foo'));
});
- it('should pass jscs', function() {
- return jscsDir(dir, 'Foo').should.be.fulfilled();
- });
-
it('should pass lint', function() {
- return jshintDir(dir, 'Foo').should.be.fulfilled();
+ return eslintDir(dir, 'Foo').should.be.fulfilled();
});
});
@@ -204,9 +160,8 @@ describe('angular-fullstack:endpoint', function() {
dir = _dir;
return Promise.all([
- copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'),
- copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'),
- copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc')
+ copyAsync(path.join(genDir, '/.eslintrc'), './.eslintrc'),
+ copyAsync(path.join(genDir, '/server/.eslintrc'), './server/.eslintrc')
]);
});
});
@@ -215,12 +170,8 @@ describe('angular-fullstack:endpoint', function() {
assert.file(getExpectedFiles.endpoint('bar', 'foo/bar'));
});
- it('should pass jscs', function() {
- return jscsDir(dir, 'foo', 'foo/bar').should.be.fulfilled();
- });
-
it('should pass lint', function() {
- return jshintDir(dir, 'foo', 'foo/bar').should.be.fulfilled();
+ return eslintDir(dir, 'foo', 'foo/bar').should.be.fulfilled();
});
});
@@ -231,9 +182,8 @@ describe('angular-fullstack:endpoint', function() {
dir = _dir;
return Promise.all([
- copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'),
- copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'),
- copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc')
+ copyAsync(path.join(genDir, '/.eslintrc'), './.eslintrc'),
+ copyAsync(path.join(genDir, '/server/.eslintrc'), './server/.eslintrc')
]);
});
});
@@ -242,12 +192,8 @@ describe('angular-fullstack:endpoint', function() {
assert.file(getExpectedFiles.endpoint('foo-bar'));
});
- it('should pass jscs', function() {
- return jscsDir(dir, 'foo-bar').should.be.fulfilled();
- });
-
it('should pass lint', function() {
- return jshintDir(dir, 'foo-bar').should.be.fulfilled();
+ return eslintDir(dir, 'foo-bar').should.be.fulfilled();
});
});
-});
\ No newline at end of file
+});
diff --git a/src/test/fixtures/.bowerrc b/src/test/fixtures/.bowerrc
deleted file mode 100644
index 69fad3580..000000000
--- a/src/test/fixtures/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "bower_components"
-}
diff --git a/src/test/fixtures/.yo-rc.json b/src/test/fixtures/.yo-rc.json
index 3f652f692..ca971c119 100644
--- a/src/test/fixtures/.yo-rc.json
+++ b/src/test/fixtures/.yo-rc.json
@@ -26,7 +26,6 @@
"models": true,
"mongooseModels": true,
"mongoose": true,
- "grunt": true,
"mocha": true,
"jasmine": false,
"expect": true
@@ -47,7 +46,8 @@
"expect",
"should",
"uirouter",
- "es6"
+ "es6",
+ "webpack"
],
"extensions": [
"babel",
diff --git a/src/test/get-expected-files.js b/src/test/get-expected-files.js
index 3543eda94..d70c7c101 100644
--- a/src/test/get-expected-files.js
+++ b/src/test/get-expected-files.js
@@ -49,25 +49,26 @@ export function app(options) {
'client/.htaccess',
'client/favicon.ico',
'client/robots.txt',
- 'client/index.html',
+ 'client/_index.html',
+ `client/polyfills.${script}`,
'client/app/app.' + script,
+ 'client/app/app.config.' + script,
+ 'client/app/app.constants.' + script,
'client/app/app.' + stylesheet,
- 'client/app/main/main.' + script,
+ 'client/app/main/main.component.' + script,
+ 'client/app/main/main.component.spec.' + script,
+ 'client/app/main/main.routes.' + script,
'client/app/main/main.' + markup,
'client/app/main/main.' + stylesheet,
- 'client/app/main/main.controller.' + script,
- 'client/app/main/main.controller.spec.' + script,
'client/assets/images/yeoman.png',
'client/components/footer/footer.' + stylesheet,
'client/components/footer/footer.' + markup,
- 'client/components/footer/footer.directive.' + script,
+ 'client/components/footer/footer.component.' + script,
'client/components/navbar/navbar.' + markup,
- 'client/components/navbar/navbar.controller.' + script,
- 'client/components/navbar/navbar.directive.' + script,
+ 'client/components/navbar/navbar.component.' + script,
'client/components/util/util.module.' + script,
'client/components/util/util.service.' + script,
- 'server/.jshintrc',
- 'server/.jshintrc-spec',
+ 'server/.eslintrc',
'server/app.js',
'server/index.js',
'server/routes.js',
@@ -89,21 +90,25 @@ export function app(options) {
'e2e/main/main.spec.js',
'e2e/components/navbar/navbar.po.js',
'.babelrc',
- '.bowerrc',
'.buildignore',
'.editorconfig',
+ '.eslintrc',
'.gitattributes',
'.gitignore',
'.travis.yml',
- '.jscsrc',
'.yo-rc.json',
- 'Gruntfile.js',
+ 'gulpfile.babel.js',
'package.json',
- 'bower.json',
'karma.conf.js',
'mocha.conf.js',
+ 'mocha.global.js',
'protractor.conf.js',
- 'README.md'
+ 'README.md',
+ 'spec.js',
+ 'webpack.build.js',
+ 'webpack.dev.js',
+ 'webpack.test.js',
+ 'webpack.make.js'
]);
/* TypeScript */
@@ -116,10 +121,15 @@ export function app(options) {
]);
} else {
files = files.concat([
- 'client/.jshintrc'
+ 'client/.eslintrc'
]);
}
+ /* Flow */
+ if(options.flow) {
+ files.push('.flowconfig');
+ }
+
/* Ui-Router */
if (options.router === 'uirouter') {
files = files.concat([
@@ -155,18 +165,22 @@ export function app(options) {
/* Authentication */
if (options.auth) {
files = files.concat([
- 'client/app/account/account.' + script,
+ 'client/app/account/index.' + script,
+ 'client/app/account/account.routes.' + script,
'client/app/account/login/login.' + markup,
+ 'client/app/account/login/index.' + script,
'client/app/account/login/login.controller.' + script,
'client/app/account/settings/settings.' + markup,
+ 'client/app/account/settings/index.' + script,
'client/app/account/settings/settings.controller.' + script,
'client/app/account/signup/signup.' + markup,
+ 'client/app/account/signup/index.' + script,
'client/app/account/signup/signup.controller.' + script,
+ 'client/app/admin/index.' + script,
'client/app/admin/admin.' + markup,
'client/app/admin/admin.' + stylesheet,
- 'client/app/admin/admin.module.' + script,
- 'client/app/admin/admin.router.' + script,
'client/app/admin/admin.controller.' + script,
+ 'client/app/admin/admin.routes.' + script,
'client/components/auth/auth.module.' + script,
'client/components/auth/auth.service.' + script,
'client/components/auth/interceptor.service.' + script,
@@ -200,11 +214,10 @@ export function app(options) {
files = files.concat([
+ 'client/components/oauth-buttons/index.' + script,
'client/components/oauth-buttons/oauth-buttons.' + stylesheet,
'client/components/oauth-buttons/oauth-buttons.' + markup,
- 'client/components/oauth-buttons/oauth-buttons.controller.' + script,
'client/components/oauth-buttons/oauth-buttons.controller.spec.' + script,
- 'client/components/oauth-buttons/oauth-buttons.directive.' + script,
'client/components/oauth-buttons/oauth-buttons.directive.spec.' + script,
'e2e/components/oauth-buttons/oauth-buttons.po.js'
]);
diff --git a/src/test/main.test.js b/src/test/main.test.js
index a2efb5ae2..c34b1ade0 100644
--- a/src/test/main.test.js
+++ b/src/test/main.test.js
@@ -15,7 +15,7 @@ import {
} from './test-helpers';
const defaultOptions = {
- buildtool: 'grunt',
+ buildtool: 'gulp',
transpiler: 'babel',
markup: 'html',
stylesheet: 'sass',
@@ -50,7 +50,7 @@ function runEndpointGen(name, opt={}) {
gen
.on('error', reject)
- .on('end', () => resolve())
+ .on('end', () => resolve());
});
}
@@ -70,20 +70,16 @@ describe('angular-fullstack:app', function() {
return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled();
});
- it('passes JSCS', function() {
- return runCmd('grunt jscs').should.be.fulfilled();
- });
-
- it('passes JSHint', function() {
- return runCmd('grunt jshint').should.be.fulfilled();
+ it('passes lint', function() {
+ return runCmd('gulp lint:scripts').should.be.fulfilled();
});
it('passes client tests', function() {
- return runCmd('grunt test:client').should.be.fulfilled();
+ return runCmd('gulp test:client').should.be.fulfilled();
});
it('passes server tests', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
describe('with a generated endpoint', function() {
@@ -94,7 +90,7 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
@@ -106,7 +102,7 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
@@ -118,7 +114,7 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
@@ -130,26 +126,25 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
if(!process.env.SKIP_E2E) {
- it('should run e2e tests successfully', function() {
+ it.skip('should run e2e tests successfully', function() {
this.retries(2);
- return runCmd('grunt test:e2e').should.be.fulfilled();
+ return runCmd('gulp test:e2e').should.be.fulfilled();
});
- it('should run e2e tests successfully for production app', function() {
+ it.skip('should run e2e tests successfully for production app', function() {
this.retries(2);
- return runCmd('grunt test:e2e:prod').should.be.fulfilled();
+ return runCmd('gulp test:e2e:prod').should.be.fulfilled();
});
}
});
describe('default settings using existing `.yo-rc.json`', function() {
var dir;
- var jscsResult;
var lintResult;
var clientTestResult;
var serverTestResult;
@@ -163,10 +158,9 @@ describe('angular-fullstack:app', function() {
}
}).then(_dir => {
dir = _dir;
- jscsResult = runCmd('grunt jscs');
- lintResult = runCmd('grunt jshint');
- clientTestResult = runCmd('grunt test:client');
- serverTestResult = runCmd('grunt test:server');
+ lintResult = runCmd('gulp lint:scripts');
+ clientTestResult = runCmd('gulp test:client');
+ serverTestResult = runCmd('gulp test:server');
});
});
@@ -176,11 +170,7 @@ describe('angular-fullstack:app', function() {
return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled();
});
- it('passes JSCS', function() {
- return jscsResult.should.be.fulfilled();
- });
-
- it('passes JSHint', function() {
+ it('passes lint', function() {
return lintResult.should.be.fulfilled();
});
@@ -195,12 +185,11 @@ describe('angular-fullstack:app', function() {
describe('with TypeScript, Jade, Jasmine, LESS, & OAuth', function() {
var dir;
- var jscsResult;
var lintResult;
var clientTestResult;
var serverTestResult;
var testOptions = {
- buildtool: 'grunt',
+ buildtool: 'gulp',
transpiler: 'ts',
markup: 'jade',
stylesheet: 'less',
@@ -217,10 +206,9 @@ describe('angular-fullstack:app', function() {
before(function() {
return runGen(testOptions).then(_dir => {
dir = _dir;
- jscsResult = runCmd('grunt jscs');
- lintResult = runCmd('grunt tslint');
- clientTestResult = runCmd('grunt test:client');
- serverTestResult = runCmd('grunt test:server');
+ lintResult = runCmd('gulp lint:scripts');
+ clientTestResult = runCmd('gulp test:client');
+ serverTestResult = runCmd('gulp test:server');
});
});
@@ -230,10 +218,6 @@ describe('angular-fullstack:app', function() {
return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled();
});
- it('passes JSCS', function() {
- return jscsResult.should.be.fulfilled();
- });
-
it('passes lint', function() {
return lintResult.should.be.fulfilled();
});
@@ -254,31 +238,30 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
if(!process.env.SKIP_E2E) {
it.skip('should run e2e tests successfully', function() {
this.retries(2);
- return runCmd('grunt test:e2e').should.be.fulfilled();
+ return runCmd('gulp test:e2e').should.be.fulfilled();
});
it.skip('should run e2e tests successfully for production app', function() {
this.retries(2);
- return runCmd('grunt test:e2e:prod').should.be.fulfilled();
+ return runCmd('gulp test:e2e:prod').should.be.fulfilled();
});
}
});
describe('with sequelize models, auth', function() {
var dir;
- var jscsResult;
var lintResult;
var clientTestResult;
var serverTestResult;
var testOptions = {
- buildtool: 'grunt',
+ buildtool: 'gulp',
transpiler: 'babel',
markup: 'jade',
stylesheet: 'css',
@@ -296,10 +279,8 @@ describe('angular-fullstack:app', function() {
beforeEach(function() {
return runGen(testOptions).then(_dir => {
dir = _dir;
- jscsResult = runCmd('grunt jscs');
- lintResult = runCmd('grunt jshint');
- clientTestResult = runCmd('grunt test:client');
- serverTestResult = runCmd('grunt test:server');
+ lintResult = runCmd('gulp lint:scripts');
+ clientTestResult = runCmd('gulp test:client');
});
});
@@ -309,10 +290,6 @@ describe('angular-fullstack:app', function() {
return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled();
});
- it('passes JSCS', function() {
- return jscsResult.should.be.fulfilled();
- });
-
it('passes lint', function() {
return lintResult.should.be.fulfilled();
});
@@ -321,11 +298,11 @@ describe('angular-fullstack:app', function() {
return clientTestResult.should.be.fulfilled();
});
- it('should run server tests successfully', function() {
- return serverTestResult.should.be.fulfilled();
+ it.skip('should run server tests successfully', function() {
+ return runCmd('gulp test:server').should.be.fulfilled();
});
- describe('with a generated endpoint', function() {
+ describe.skip('with a generated endpoint', function() {
beforeEach(function() {
return readJSON(path.join(dir, '.yo-rc.json')).then(config => {
return runEndpointGen('foo', {config: config['generator-angular-fullstack']});
@@ -333,31 +310,30 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
if(!process.env.SKIP_E2E) {
it.skip('should run e2e tests successfully', function() {
this.retries(2);
- return runCmd('grunt test:e2e').should.be.fulfilled();
+ return runCmd('gulp test:e2e').should.be.fulfilled();
});
it.skip('should run e2e tests successfully for production app', function() {
this.retries(2);
- return runCmd('grunt test:e2e:prod').should.be.fulfilled();
+ return runCmd('gulp test:e2e:prod').should.be.fulfilled();
});
}
});
describe('with TypeScript, Mocha + Chai (should) and no server options', function() {
var dir;
- var jscsResult;
var lintResult;
var clientTestResult;
var serverTestResult;
var testOptions = {
- buildtool: 'grunt',
+ buildtool: 'gulp',
transpiler: 'ts',
markup: 'jade',
stylesheet: 'stylus',
@@ -375,10 +351,9 @@ describe('angular-fullstack:app', function() {
beforeEach(function() {
return runGen(testOptions).then(_dir => {
dir = _dir;
- jscsResult = runCmd('grunt jscs');
- lintResult = runCmd('grunt tslint');
- clientTestResult = runCmd('grunt test:client');
- serverTestResult = runCmd('grunt test:server');
+ lintResult = runCmd('gulp lint:scripts');
+ clientTestResult = runCmd('gulp test:client');
+ serverTestResult = runCmd('gulp test:server');
});
});
@@ -388,10 +363,6 @@ describe('angular-fullstack:app', function() {
return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled();
});
- it('passes JSCS', function() {
- return jscsResult.should.be.fulfilled();
- });
-
it('passes lint', function() {
return lintResult.should.be.fulfilled();
});
@@ -412,19 +383,19 @@ describe('angular-fullstack:app', function() {
});
it('should run server tests successfully', function() {
- return runCmd('grunt test:server').should.be.fulfilled();
+ return runCmd('gulp test:server').should.be.fulfilled();
});
});
if(!process.env.SKIP_E2E) {
it.skip('should run e2e tests successfully', function() {
this.retries(2);
- return runCmd('grunt test:e2e').should.be.fulfilled();
+ return runCmd('gulp test:e2e').should.be.fulfilled();
});
it.skip('should run e2e tests successfully for production app', function() {
this.retries(2);
- return runCmd('grunt test:e2e:prod').should.be.fulfilled();
+ return runCmd('gulp test:e2e:prod').should.be.fulfilled();
});
}
});
diff --git a/src/test/pre.test.js b/src/test/pre.test.js
index 61c88de27..d30652555 100644
--- a/src/test/pre.test.js
+++ b/src/test/pre.test.js
@@ -7,10 +7,6 @@ describe('test fixtures', function() {
assert.file([path.join(__dirname, 'fixtures/package.json')]);
});
- it('should have .bowerrc & bower.json in fixtures', function() {
- assert.file([path.join(__dirname, 'fixtures/bower.json'), path.join(__dirname, 'fixtures/.bowerrc')]);
- });
-
it('should have .yo-rc.json in fixtures', function() {
assert.file([path.join(__dirname, 'fixtures/.yo-rc.json')]);
});
@@ -24,14 +20,4 @@ describe('test fixtures', function() {
});
assert.file(deps);
});
-
- it('should have all bower packages in fixtures/bower_components', function() {
- var bowerJson = require('./fixtures/bower.json');
- var deps = Object.keys(bowerJson.dependencies);
- deps = deps.concat(Object.keys(bowerJson.devDependencies));
- deps = deps.map(function(dep) {
- return path.join(__dirname, 'fixtures', 'bower_components', dep);
- });
- assert.file(deps);
- });
-});
\ No newline at end of file
+});
diff --git a/src/test/test-helpers.js b/src/test/test-helpers.js
index ffd69c0a8..16978ab07 100644
--- a/src/test/test-helpers.js
+++ b/src/test/test-helpers.js
@@ -33,7 +33,7 @@ export function copyAsync(src, dest) {
*/
export function runCmd(cmd) {
return new Promise((resolve, reject) => {
- exec(cmd, {}, function(err, stdout, stderr) {
+ exec(cmd, {maxBuffer: 1024 * 500}, function(err, stdout, stderr) {
if(err) {
console.error(stdout);
return reject(err);
@@ -100,9 +100,6 @@ export function runGen(prompts, opts={}) {
if(DEBUG) console.log(`TEMP DIR: ${dir}`);
let promises = [
- fs.mkdirAsync(dir + '/client').then(() => {
- return fs.symlinkAsync(__dirname + '/fixtures/bower_components', dir + '/client/bower_components');
- }),
fs.symlinkAsync(__dirname + '/fixtures/node_modules', dir + '/node_modules')
];
diff --git a/templates/app/.bowerrc b/templates/app/.bowerrc
deleted file mode 100644
index 666f34745..000000000
--- a/templates/app/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "client/bower_components"
-}
diff --git a/templates/app/.eslintrc b/templates/app/.eslintrc
new file mode 100644
index 000000000..ef5d7de07
--- /dev/null
+++ b/templates/app/.eslintrc
@@ -0,0 +1,251 @@
+{
+ "parser": "babel-eslint",
+ "env": {
+ "es6": true
+ },
+ "globals": {},
+ "plugins": [],
+ "rules": {
+ //Possible Errors
+ "comma-dangle": 0, //disallow or enforce trailing commas
+ "no-cond-assign": 2, //disallow assignment in conditional expressions
+ "no-console": 0, //disallow use of console in the node environment
+ "no-constant-condition": 1, //disallow use of constant expressions in conditions
+ "no-control-regex": 2, //disallow control characters in regular expressions
+ "no-debugger": 2, //disallow use of debugger
+ "no-dupe-args": 2, //disallow duplicate arguments in functions
+ "no-dupe-keys": 2, //disallow duplicate keys when creating object literals
+ "no-duplicate-case": 0, //disallow a duplicate case label.
+ "no-empty-character-class": 2, //disallow the use of empty character classes in regular expressions
+ "no-empty": 2, //disallow empty statements
+ "no-ex-assign": 2, //disallow assigning to the exception in a catch block
+ "no-extra-boolean-cast": 2, //disallow double-negation boolean casts in a boolean context
+ "no-extra-parens": 1, //disallow unnecessary parentheses
+ "no-extra-semi": 2, //disallow unnecessary semicolons
+ "no-func-assign": 2, //disallow overwriting functions written as function declarations
+ "no-inner-declarations": 1, //disallow function or variable declarations in nested blocks
+ "no-invalid-regexp": 2, //disallow invalid regular expression strings in the RegExp constructor
+ "no-irregular-whitespace": 2, //disallow irregular whitespace outside of strings and comments
+ "no-negated-in-lhs": 2, //disallow negation of the left operand of an in expression
+ "no-obj-calls": 2, //disallow the use of object properties of the global object (Math and JSON) as functions
+ "no-prototype-builtins": 0, //Disallow use of Object.prototypes builtins directly
+ "no-regex-spaces": 2, //disallow multiple spaces in a regular expression literal
+ "no-sparse-arrays": 1, //disallow sparse arrays
+ "no-unexpected-multiline": 2, //Avoid code that looks like two expressions but is actually one
+ "no-unreachable": 2, //disallow unreachable statements after a return, throw, continue, or break statement
+ "no-unsafe-finally": 2, //disallow control flow statements in finally blocks
+ "use-isnan": 2, //disallow comparisons with the value NaN
+ "valid-jsdoc": 0, //Ensure JSDoc comments are valid
+ "valid-typeof": 2, //Ensure that the results of typeof are compared against a valid string
+
+ //Best Practices
+ "accessor-pairs": 0, //Enforces getter/setter pairs in objects
+ "array-callback-return": 2, //Enforces return statements in callbacks of array's methods
+ "block-scoped-var": 0, //treat var statements as if they were block scoped
+ "complexity": 0, //specify the maximum cyclomatic complexity allowed in a program
+ "consistent-return": 0, //require return statements to either always or never specify values
+ "curly": [2, "multi-line"], //specify curly brace conventions for all control statements
+ "default-case": 0, //require default case in switch statements
+ "dot-location": [2, "property"], //enforces consistent newlines before or after dots
+ "dot-notation": 2, //encourages use of dot notation whenever possible
+ "eqeqeq": 0, //require the use of === and !==
+ "guard-for-in": 0, //make sure for-in loops have an if statement
+ "no-alert": 2, //disallow the use of alert, confirm, and prompt
+ "no-caller": 0, //disallow use of arguments.caller or arguments.callee
+ "no-case-declarations": 0, //disallow lexical declarations in case clauses
+ "no-div-regex": 2, //disallow division operators explicitly at beginning of regular expression
+ "no-else-return": 0, //disallow else after a return in an if
+ "no-empty-function": 0, //disallow use of empty functions
+ "no-empty-pattern": 2, //disallow use of empty destructuring patterns
+ "no-eq-null": 2, //disallow comparisons to null without a type-checking operator
+ "no-eval": 2, //disallow use of eval()
+ "no-extend-native": 0, //disallow adding to native types
+ "no-extra-bind": 1, //disallow unnecessary function binding
+ "no-extra-label": 2, //disallow unnecessary labels
+ "no-fallthrough": 0, //disallow fallthrough of case statements
+ "no-floating-decimal": 0, //disallow the use of leading or trailing decimal points in numeric literals
+ "no-implicit-coercion": 0, //disallow the type conversions with shorter notations
+ "no-implicit-globals": 0, //disallow var and named functions in global scope
+ "no-implied-eval": 2, //disallow use of eval()-like methods
+ "no-invalid-this": 1, //disallow this keywords outside of classes or class-like objects
+ "no-iterator": 2, //disallow usage of __iterator__ property
+ "no-labels": 2, //disallow use of labeled statements
+ "no-lone-blocks": 2, //disallow unnecessary nested blocks
+ "no-loop-func": 2, //disallow creation of functions within loops
+ "no-magic-numbers": 0, //disallow the use of magic numbers
+ "no-multi-spaces": 2, //disallow use of multiple spaces
+ "no-multi-str": 0, //disallow use of multiline strings
+ "no-native-reassign": 2, //disallow reassignments of native objects
+ "no-new-func": 1, //disallow use of new operator for Function object
+ "no-new-wrappers": 2, //disallows creating new instances of String,Number, and Boolean
+ "no-new": 2, //disallow use of the new operator when not part of an assignment or comparison
+ "no-octal-escape": 0, //disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
+ "no-octal": 0, //disallow use of octal literals
+ "no-param-reassign": 0, //disallow reassignment of function parameters
+ "no-process-env": 1, //disallow use of process.env
+ "no-proto": 2, //disallow usage of __proto__ property
+ "no-redeclare": 2, //disallow declaring the same variable more than once
+ "no-return-assign": 2, //disallow use of assignment in return statement
+ "no-script-url": 2, //disallow use of javascript: urls.
+ "no-self-assign": 2, //disallow assignments where both sides are exactly the same
+ "no-self-compare": 2, //disallow comparisons where both sides are exactly the same
+ "no-sequences": 2, //disallow use of the comma operator
+ "no-throw-literal": 2, //restrict what can be thrown as an exception
+ "no-unmodified-loop-condition": 0, //disallow unmodified conditions of loops
+ "no-unused-expressions": 0, //disallow usage of expressions in statement position
+ "no-unused-labels": 2, //disallow unused labels
+ "no-useless-call": 2, //disallow unnecessary .call() and .apply()
+ "no-useless-concat": 2, //disallow unnecessary concatenation of literals or template literals
+ "no-useless-escape": 2, //disallow unnecessary escape characters
+ "no-void": 0, //disallow use of the void operator
+ "no-warning-comments": 1, //disallow usage of configurable warning terms in comments (e.g. TODO or FIXME)
+ "no-with": 2, //disallow use of the with statement
+ "radix": 2, //require use of the second argument for parseInt()
+ "vars-on-top": 0, //require declaration of all vars at the top of their containing scope
+ "wrap-iife": 2, //require immediate function invocation to be wrapped in parentheses
+ "yoda": 2, //require or disallow Yoda conditions
+
+ //Strict Mode
+ "strict": 0, //controls location of Use Strict Directives
+
+ //Variables
+ "init-declarations": 0, //enforce or disallow variable initializations at definition
+ "no-catch-shadow": 2, //disallow the catch clause parameter name being the same as a variable in the outer scope
+ "no-delete-var": 2, //disallow deletion of variables
+ "no-label-var": 2, //disallow labels that share a name with a variable
+ "no-restricted-globals": 0, //restrict usage of specified global variables
+ "no-shadow-restricted-names": 2, //disallow shadowing of names such as arguments
+ "no-shadow": [2, {"allow": ["err"]}], //disallow declaration of variables already declared in the outer scope
+ "no-undef-init": 2, //disallow use of undefined when initializing variables
+ "no-undef": 2, //disallow use of undeclared variables unless mentioned in a /*global */ block
+ "no-undefined": 0, //disallow use of undefined variable
+ "no-unused-vars": 2, //disallow declaration of variables that are not used in the code
+ "no-use-before-define": 0, //disallow use of variables before they are defined
+
+ //Node.js and CommonJS
+ "callback-return": 2, //enforce return after a callback
+ "global-require": 0, //enforce require() on top-level module scope
+ "handle-callback-err": 2, //enforce error handling in callbacks
+ "no-mixed-requires": 2, //disallow mixing regular variable and require declarations
+ "no-new-require": 2, //disallow use of new operator with the require function
+ "no-path-concat": 2, //disallow string concatenation with __dirname and __filename
+ "no-process-exit": 2, //disallow process.exit()
+ "no-restricted-imports": 0, //restrict usage of specified node imports
+ "no-restricted-modules": 0, //restrict usage of specified node modules
+ "no-sync": 1, //disallow use of synchronous methods
+
+ //Stylistic Issues
+ "array-bracket-spacing": [2, "never"], //enforce spacing inside array brackets
+ "block-spacing": 0, //disallow or enforce spaces inside of single line blocks
+ "brace-style": 2, //enforce one true brace style
+ "camelcase": 1, //require camel case names
+ "comma-spacing": [2, {"before": false, "after": true}], //enforce spacing before and after comma
+ "comma-style": 2, //enforce one true comma style
+ "computed-property-spacing": 2, //require or disallow padding inside computed properties
+ "consistent-this": 2, //enforce consistent naming when capturing the current execution context
+ "eol-last": 2, //enforce newline at the end of file, with no multiple empty lines
+ "func-names": 0, //require function expressions to have a name
+ "func-style": 0, //enforce use of function declarations or expressions
+ "id-blacklist": 0, //blacklist certain identifiers to prevent them being used
+ "id-length": 0, //this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
+ "id-match": 0, //require identifiers to match the provided regular expression
+ "indent": ["error", 2], //specify tab or space width for your code
+ "jsx-quotes": 0, //specify whether double or single quotes should be used in JSX attributes
+ "key-spacing": 2, //enforce spacing between keys and values in object literal properties
+ "keyword-spacing": [2, {
+ "before": true,
+ "after": true,
+ "overrides": {
+ "if": {"after": false},
+ "for": {"after": false},
+ "while": {"after": false},
+ "catch": {"after": false}
+ }
+ }], //enforce spacing before and after keywords
+ "linebreak-style": 2, //disallow mixed 'LF' and 'CRLF' as linebreaks
+ "lines-around-comment": 0, //enforce empty lines around comments
+ "max-depth": 1, //specify the maximum depth that blocks can be nested
+ "max-len": [1, 200], //specify the maximum length of a line in your program
+ "max-lines": 0, //enforce a maximum file length
+ "max-nested-callbacks": 2, //specify the maximum depth callbacks can be nested
+ "max-params": 0, //limits the number of parameters that can be used in the function declaration.
+ "max-statements": 0, //specify the maximum number of statement allowed in a function
+ "max-statements-per-line": 0, //enforce a maximum number of statements allowed per line
+ "new-cap": 0, //require a capital letter for constructors
+ "new-parens": 2, //disallow the omission of parentheses when invoking a constructor with no arguments
+ "newline-after-var": 0, //require or disallow an empty newline after variable declarations
+ "newline-before-return": 0, //require newline before return statement
+ "newline-per-chained-call": 0, //enforce newline after each call when chaining the calls
+ "no-array-constructor": 2, //disallow use of the Array constructor
+ "no-bitwise": 0, //disallow use of bitwise operators
+ "no-continue": 0, //disallow use of the continue statement
+ "no-inline-comments": 0, //disallow comments inline after code
+ "no-lonely-if": 2, //disallow if as the only statement in an else block
+ "no-mixed-operators": 0, //disallow mixes of different operators
+ "no-mixed-spaces-and-tabs": 2, //disallow mixed spaces and tabs for indentation
+ "no-multiple-empty-lines": 2, //disallow multiple empty lines
+ "no-negated-condition": 0, //disallow negated conditions
+ "no-nested-ternary": 0, //disallow nested ternary expressions
+ "no-new-object": 2, //disallow the use of the Object constructor
+ "no-plusplus": 0, //disallow use of unary operators, ++ and --
+ "no-restricted-syntax": 0, //disallow use of certain syntax in code
+ "no-spaced-func": 2, //disallow space between function identifier and application
+ "no-ternary": 0, //disallow the use of ternary operators
+ "no-trailing-spaces": 2, //disallow trailing whitespace at the end of lines
+ "no-underscore-dangle": 0, //disallow dangling underscores in identifiers
+ "no-unneeded-ternary": 2, //disallow the use of ternary operators when a simpler alternative exists
+ "no-whitespace-before-property": 2, //disallow whitespace before properties
+ "object-curly-newline": 0, //enforce consistent line breaks inside braces
+ "object-curly-spacing": 0, //require or disallow padding inside curly braces
+ "object-property-newline": 0, //enforce placing object properties on separate lines
+ "one-var": [2, "never"], //require or disallow one variable declaration per function
+ "one-var-declaration-per-line": 2, //require or disallow an newline around variable declarations
+ "operator-assignment": 0, //require assignment operator shorthand where possible or prohibit it entirely
+ "operator-linebreak": [1, "before"], //enforce operators to be placed before or after line breaks
+ "padded-blocks": [2, "never"], //enforce padding within blocks
+ "quote-props": [2, "as-needed"], //require quotes around object literal property names
+ "quotes": [2, "single"], //specify whether backticks, double or single quotes should be used
+ "require-jsdoc": 0, //Require JSDoc comment
+ "semi-spacing": 2, //enforce spacing before and after semicolons
+ "sort-imports": 0, //sort import declarations within module
+ "semi": 2, //require or disallow use of semicolons instead of ASI
+ "sort-vars": 0, //sort variables within the same declaration block
+ "space-before-blocks": 2, //require or disallow a space before blocks
+ "space-before-function-paren": [2, "never"], //require or disallow a space before function opening parenthesis
+ "space-in-parens": 2, //require or disallow spaces inside parentheses
+ "space-infix-ops": 2, //require spaces around operators
+ "space-unary-ops": 2, //require or disallow spaces before/after unary operators
+ "spaced-comment": 0, //require or disallow a space immediately following the // or /* in a comment
+ "unicode-bom": 0, //require or disallow the Unicode BOM
+ "wrap-regex": 0, //require regex literals to be wrapped in parentheses
+
+ //ECMAScript 6
+ "arrow-body-style": [2, "as-needed"], //require braces in arrow function body
+ "arrow-parens": [2, "as-needed"], //require parens in arrow function arguments
+ "arrow-spacing": 2, //require space before/after arrow function's arrow
+ "constructor-super": 2, //verify calls of super() in constructors
+ "generator-star-spacing": 0, //enforce spacing around the * in generator functions
+ "no-class-assign": 2, //disallow modifying variables of class declarations
+ "no-confusing-arrow": 2, //disallow arrow functions where they could be confused with comparisons
+ "no-const-assign": 2, //disallow modifying variables that are declared using const
+ "no-dupe-class-members": 2, //disallow duplicate name in class members
+ "no-duplicate-imports": 2, //disallow duplicate module imports
+ "no-new-symbol": 2, //disallow use of the new operator with the Symbol object
+ "no-this-before-super": 2, //disallow use of this/super before calling super() in constructors.
+ "no-useless-computed-key": 2, //disallow unnecessary computed property keys in object literals
+ "no-useless-constructor": 2, //disallow unnecessary constructor
+ "no-useless-rename": 2, //disallow renaming import, export, and destructured assignments to the same name
+ "no-var": 0, //require let or const instead of var
+ "object-shorthand": 1, //require method and property shorthand syntax for object literals
+ "prefer-arrow-callback": 0, //suggest using arrow functions as callbacks
+ "prefer-const": 0, //suggest using const declaration for variables that are never modified after declared
+ "prefer-reflect": 1, //suggest using Reflect methods where applicable
+ "prefer-rest-params": 1, //suggest using the rest parameters instead of arguments
+ "prefer-spread": 1, //suggest using the spread operator instead of .apply().
+ "prefer-template": 1, //suggest using template literals instead of strings concatenation
+ "require-yield": 2, //disallow generator functions that do not have yield
+ "rest-spread-spacing": ["error", "never"], //enforce spacing between rest and spread operators and their expressions
+ "template-curly-spacing": 2, //enforce spacing around embedded expressions of template strings
+ "yield-star-spacing": [2, "after"] //enforce spacing around the * in yield* expressions
+ }
+}
diff --git a/templates/app/.flowconfig(flow) b/templates/app/.flowconfig(flow)
new file mode 100644
index 000000000..e69de29bb
diff --git a/templates/app/.jscsrc b/templates/app/.jscsrc
deleted file mode 100644
index e05f83c6c..000000000
--- a/templates/app/.jscsrc
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "excludeFiles": [
- "client/app/app.constant.js"
- ],
- "maximumLineLength": {
- "value": 100,
- "allowComments": true,
- "allowRegex": true
- },
- "disallowMixedSpacesAndTabs": true,
- "disallowMultipleLineStrings": true,
- "disallowNewlineBeforeBlockStatements": true,
- "disallowSpaceAfterObjectKeys": true,
- "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"],
- "disallowSpaceBeforeBinaryOperators": [","],
- "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
- "disallowSpacesInAnonymousFunctionExpression": {
- "beforeOpeningRoundBrace": true
- },
- "disallowSpacesInFunctionDeclaration": {
- "beforeOpeningRoundBrace": true
- },
- "disallowSpacesInNamedFunctionExpression": {
- "beforeOpeningRoundBrace": true
- },
- "disallowSpacesInsideArrayBrackets": true,
- "disallowSpacesInsideParentheses": true,
- "disallowTrailingComma": true,
- "disallowTrailingWhitespace": true,
- "requireCommaBeforeLineBreak": true,
- "requireLineFeedAtFileEnd": true,
- "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
- "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
- "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
- "requireSpaceBeforeBlockStatements": true,
- "requireSpacesInConditionalExpression": {
- "afterTest": true,
- "beforeConsequent": true,
- "afterConsequent": true,
- "beforeAlternate": true
- },
- "requireSpacesInFunction": {
- "beforeOpeningCurlyBrace": true
- },
- "validateLineBreaks": "LF",
- "validateParameterSeparator": ", "
-}
diff --git a/templates/app/.travis.yml b/templates/app/.travis.yml
index dba453567..2f15c52e1 100644
--- a/templates/app/.travis.yml
+++ b/templates/app/.travis.yml
@@ -1,12 +1,12 @@
language: node_js
node_js:
- - 4.2.3
+ - 4.4.6
matrix:
fast_finish: true
allow_failures:
- - node_js: 5.1.1
+ - node_js: 5.12.0
before_script:
- - npm install -g bower grunt-cli<% if (filters.sass) { %>
+ - npm install -g gulp-cli<% if (filters.sass) { %>
- gem install sass<% } %>
- bower install
services: mongodb
diff --git a/templates/app/Gruntfile(grunt).js b/templates/app/Gruntfile(grunt).js
deleted file mode 100644
index dbe99a0bf..000000000
--- a/templates/app/Gruntfile(grunt).js
+++ /dev/null
@@ -1,987 +0,0 @@
-// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= rootGeneratorName() %> <%= rootGeneratorVersion() %>
-'use strict';
-
-module.exports = function(grunt) {
- var localConfig;
- try {
- localConfig = require('./server/config/local.env');
- } catch(e) {
- localConfig = {};
- }
-
- // Load grunt tasks automatically, when needed
- require('jit-grunt')(grunt, {
- express: 'grunt-express-server',
- useminPrepare: 'grunt-usemin',
- ngtemplates: 'grunt-angular-templates',
- cdnify: 'grunt-google-cdn',
- protractor: 'grunt-protractor-runner',
- buildcontrol: 'grunt-build-control',
- istanbul_check_coverage: 'grunt-mocha-istanbul',
- ngconstant: 'grunt-ng-constant'
- });
-
- // Time how long tasks take. Can help when optimizing build times
- require('time-grunt')(grunt);
-
- // Define the configuration for all the tasks
- grunt.initConfig({
- // Project settings
- pkg: grunt.file.readJSON('package.json'),
- yeoman: {
- // configurable paths
- client: require('./bower.json').appPath || 'client',
- server: 'server',
- dist: 'dist'
- },
- express: {
- options: {
- port: process.env.PORT || <%= devPort %>
- },
- dev: {
- options: {
- script: '<%%= yeoman.server %>',
- debug: true
- }
- },
- prod: {
- options: {
- port: process.env.PORT || <%= prodPort %>,
- script: '<%%= yeoman.dist %>/<%%= yeoman.server %>'
- }
- }
- },
- open: {
- server: {
- url: 'http://localhost:<%%= express.options.port %>'
- }
- },
- watch: {<% if(filters.babel) { %>
- babel: {
- files: ['<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).js'],
- tasks: ['newer:babel:client']
- },<% } %><% if(filters.ts) { %>
- ts: {
- files: ['<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).ts'],
- tasks: ['ts:client']
- },<% } %>
- ngconstant: {
- files: ['<%%= yeoman.server %>/config/environment/shared.js'],
- tasks: ['ngconstant']
- },
- injectJS: {
- files: [
- '<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).<%= scriptExt %>',
- '!<%%= yeoman.client %>/app/app.js'
- ],
- tasks: ['injector:scripts']
- },
- injectCss: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.css'],
- tasks: ['injector:css']
- },
- mochaTest: {
- files: ['<%%= yeoman.server %>/**/*.{spec,integration}.<%= scriptExt %>'],
- tasks: ['env:test', 'mochaTest']
- },
- jsTest: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.{spec,mock}.<%= scriptExt %>'],
- tasks: [<% if(filters.babel) { %>'newer:jshint:all'<% } if(filters.ts) { %>'newer:tslint:all', 'newer:ts:client_test',<% } %>, 'wiredep:test', 'karma']
- },<% if(filters.stylus) { %>
- injectStylus: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.styl'],
- tasks: ['injector:stylus']
- },
- stylus: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.styl'],
- tasks: ['stylus', 'postcss']
- },<% } if(filters.sass) { %>
- injectSass: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'],
- tasks: ['injector:sass']
- },
- sass: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'],
- tasks: ['sass', 'postcss']
- },<% } if(filters.less) { %>
- injectLess: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.less'],
- tasks: ['injector:less']
- },
- less: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.less'],
- tasks: ['less', 'postcss']
- },<% } if(filters.jade) { %>
- jade: {
- files: ['<%%= yeoman.client %>/{app,components}/**/*.jade'],
- tasks: ['jade']
- },<% } %>
- gruntfile: {
- files: ['Gruntfile.js']
- },
- livereload: {
- files: [
- '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.{css,html}',
- '{.tmp,<%%= yeoman.client %>}/{app,components}/**/!(*.spec|*.mock).<%= scriptExt %>',
- '<%%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}'
- ],
- options: {
- livereload: true
- }
- },
- express: {
- files: ['<%%= yeoman.server %>/**/*.{js,json}'],
- tasks: ['express:dev', 'wait'],
- options: {
- livereload: true,
- spawn: false //Without this option specified express won't be reloaded
- }
- },
- bower: {
- files: ['bower.json'],
- tasks: ['wiredep']
- },
- },
- <%_ if(!filters.ts) { _%>
-
- // Make sure code styles are up to par and there are no obvious mistakes
- jshint: {
- options: {
- jshintrc: '<%%= yeoman.client %>/.jshintrc',
- reporter: require('jshint-stylish')
- },
- server: {
- options: {
- jshintrc: '<%%= yeoman.server %>/.jshintrc'
- },
- src: ['<%%= yeoman.server %>/**/!(*.spec|*.integration).js']
- },
- serverTest: {
- options: {
- jshintrc: '<%%= yeoman.server %>/.jshintrc-spec'
- },
- src: ['<%%= yeoman.server %>/**/*.{spec,integration}.js']
- },
- all: ['<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock|app.constant).js'],
- test: {
- src: ['<%%= yeoman.client %>/{app,components}/**/*.{spec,mock}.js']
- }
- },<% } %>
- <%_ if(filters.ts) { _%>
-
- tslint: {
- options: {
- configuration: '<%%= yeoman.client %>/tslint.json'
- },
- all: {
- src: ['<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).ts']
- }
- },<% } %>
-
- jscs: {
- options: {
- config: ".jscsrc"
- },
- main: {
- files: {
- src: [
- '<%%= yeoman.client %>/app/**/*.js',
- '<%%= yeoman.server %>/**/*.js'
- ]
- }
- }
- },
-
- // Empties folders to start fresh
- clean: {
- dist: {
- files: [{
- dot: true,
- src: [
- '.tmp',
- '<%%= yeoman.dist %>/!(.git*|.openshift|Procfile)**'
- ]
- }]
- },
- server: '.tmp'
- },
-
- // Add vendor prefixed styles
- postcss: {
- options: {
- map: true,
- processors: [
- require('autoprefixer')({browsers: ['last 2 version']})
- ]
- },
- dist: {
- files: [{
- expand: true,
- cwd: '.tmp/',
- src: '{,*/}*.css',
- dest: '.tmp/'
- }]
- }
- },
-
- // Debugging with node inspector
- 'node-inspector': {
- custom: {
- options: {
- 'web-host': 'localhost'
- }
- }
- },
-
- // Use nodemon to run server in debug mode with an initial breakpoint
- nodemon: {
- debug: {
- script: '<%%= yeoman.server %>',
- options: {
- nodeArgs: ['--debug-brk'],
- env: {
- PORT: process.env.PORT || <%= devPort %>
- },
- callback: function(nodemon) {
- nodemon.on('log', function(event) {
- console.log(event.colour);
- });
-
- // opens browser on initial server start
- nodemon.on('config:update', function() {
- setTimeout(function() {
- require('open')('http://localhost:<%= devPort %>/debug?port=<%= debugPort %>');
- }, 500);
- });
- }
- }
- }
- },
-
- // Automatically inject Bower components into the app and karma.conf.js
- wiredep: {
- options: {
- 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/<% }}} %>
- ]
- },
- client: {
- src: '<%%= yeoman.client %>/index.html',
- ignorePath: '<%%= yeoman.client %>/',
- },
- test: {
- src: './karma.conf.js',
- devDependencies: true
- }
- },
-
- // Renames files for browser caching purposes
- filerev: {
- dist: {
- src: [
- '<%%= yeoman.dist %>/<%%= yeoman.client %>/!(bower_components){,*/}*.{js,css}',
- '<%%= yeoman.dist %>/<%%= yeoman.client %>/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
- ]
- }
- },
-
- // Reads HTML for usemin blocks to enable smart builds that automatically
- // concat, minify and revision files. Creates configurations in memory so
- // additional tasks can operate on them
- useminPrepare: {
- html: ['<%%= yeoman.client %>/index.html'],
- options: {
- dest: '<%%= yeoman.dist %>/<%%= yeoman.client %>'
- }
- },
-
- // Performs rewrites based on rev and the useminPrepare configuration
- usemin: {
- html: ['<%%= yeoman.dist %>/<%%= yeoman.client %>/{,!(bower_components)/**/}*.html'],
- css: ['<%%= yeoman.dist %>/<%%= yeoman.client %>/!(bower_components){,*/}*.css'],
- js: ['<%%= yeoman.dist %>/<%%= yeoman.client %>/!(bower_components){,*/}*.js'],
- options: {
- assetsDirs: [
- '<%%= yeoman.dist %>/<%%= yeoman.client %>',
- '<%%= yeoman.dist %>/<%%= yeoman.client %>/assets/images'
- ],
- // This is so we update image references in our ng-templates
- patterns: {
- css: [
- [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the CSS to reference our revved images']
- ],
- js: [
- [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
- ]
- }
- }
- },
-
- // The following *-min tasks produce minified files in the dist folder
- imagemin: {
- dist: {
- files: [{
- expand: true,
- cwd: '<%%= yeoman.client %>/assets/images',
- src: '{,*/}*.{png,jpg,jpeg,gif,svg}',
- dest: '<%%= yeoman.dist %>/<%%= yeoman.client %>/assets/images'
- }]
- }
- },
-
- // Allow the use of non-minsafe AngularJS files. Automatically makes it
- // minsafe compatible so Uglify does not destroy the ng references
- ngAnnotate: {
- dist: {
- files: [{
- expand: true,
- cwd: '.tmp/concat',
- src: '**/*.js',
- dest: '.tmp/concat'
- }]
- }
- },
-
- // Dynamically generate angular constant `appConfig` from
- // `server/config/environment/shared.js`
- ngconstant: {
- options: {
- name: '<%= scriptAppName %>.constants',
- dest: '<%%= yeoman.client %>/app/app.constant.js',
- deps: [],
- wrap: true,
- configPath: '<%%= yeoman.server %>/config/environment/shared'
- },
- app: {
- constants: function() {
- return {
- appConfig: require('./' + grunt.config.get('ngconstant.options.configPath'))
- };
- }
- }
- },
-
- // Package all the html partials into a single javascript payload
- ngtemplates: {
- options: {
- // This should be the name of your apps angular module
- module: '<%= scriptAppName %>',
- htmlmin: {
- collapseBooleanAttributes: true,
- collapseWhitespace: true,
- removeAttributeQuotes: true,
- removeEmptyAttributes: true,
- removeRedundantAttributes: true,
- removeScriptTypeAttributes: true,
- removeStyleLinkTypeAttributes: true
- },
- usemin: 'app/app.js'
- },
- main: {
- cwd: '<%%= yeoman.client %>',
- src: ['{app,components}/**/*.html'],
- dest: '.tmp/templates.js'
- },
- tmp: {
- cwd: '.tmp',
- src: ['{app,components}/**/*.html'],
- dest: '.tmp/tmp-templates.js'
- }
- },
-
- // Replace Google CDN references
- cdnify: {
- dist: {
- html: ['<%%= yeoman.dist %>/<%%= yeoman.client %>/*.html']
- }
- },
-
- // Copies remaining files to places other tasks can use
- copy: {
- dist: {
- files: [{
- expand: true,
- dot: true,
- cwd: '<%%= yeoman.client %>',
- dest: '<%%= yeoman.dist %>/<%%= yeoman.client %>',
- src: [
- '*.{ico,png,txt}',
- '.htaccess',
- 'bower_components/**/*',
- 'assets/images/{,*/}*.{webp}',
- 'assets/fonts/**/*',
- 'index.html'
- ]
- }, {
- expand: true,
- cwd: '.tmp/images',
- dest: '<%%= yeoman.dist %>/<%%= yeoman.client %>/assets/images',
- src: ['generated/*']
- }, {
- expand: true,
- dest: '<%%= yeoman.dist %>',
- src: [
- 'package.json',
- '<%%= yeoman.server %>/**/*',
- '!<%%= yeoman.server %>/config/local.env.sample.js'
- ]
- }]
- },
- styles: {
- expand: true,
- cwd: '<%%= yeoman.client %>',
- dest: '.tmp/',
- src: ['{app,components}/**/*.css']
- }<% if(filters.ts) { %>,
- constant: {
- expand: true,
- cwd: '<%%= yeoman.client %>',
- dest: '.tmp/',
- src: ['app/app.constant.js']
- }<% } %>
- },
-
- buildcontrol: {
- options: {
- dir: '<%%= yeoman.dist %>',
- commit: true,
- push: true,
- connectCommits: false,
- message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
- },
- heroku: {
- options: {
- remote: 'heroku',
- branch: 'master'
- }
- },
- openshift: {
- options: {
- remote: 'openshift',
- branch: 'master'
- }
- }
- },
-
- // Run some tasks in parallel to speed up the build process
- concurrent: {
- pre: [<% if(filters.stylus) { %>
- 'injector:stylus',<% } if(filters.less) { %>
- 'injector:less',<% } if(filters.sass) { %>
- 'injector:sass',<% } %>
- 'ngconstant'<% if(filters.ts) { %>,
- 'copy:constant'<% } %>
- ],
- server: [<% if(filters.babel) { %>
- 'newer:babel:client',<% } if(filters.ts) { %>
- 'ts:client',
- 'copy:constant',<% } if(filters.jade) { %>
- 'jade',<% } if(filters.stylus) { %>
- 'stylus',<% } if(filters.sass) { %>
- 'sass',<% } if(filters.less) { %>
- 'less',<% } %>
- ],
- test: [<% if(filters.babel) { %>
- 'newer:babel:client',<% } if(filters.ts) { %>
- 'ts:client',
- 'copy:constant',<% } if(filters.ts) { %>
- 'ts:client_test',<% } if(filters.jade) { %>
- 'jade',<% } if(filters.stylus) { %>
- 'stylus',<% } if(filters.sass) { %>
- 'sass',<% } if(filters.less) { %>
- 'less',<% } %>
- ],
- debug: {
- tasks: [
- 'nodemon',
- 'node-inspector'
- ],
- options: {
- logConcurrentOutput: true
- }
- },
- dist: [<% if(filters.babel) { %>
- 'newer:babel:client',<% } if(filters.ts) { %>
- 'ts:client',
- 'copy:constant',<% } if(filters.jade) { %>
- 'jade',<% } if(filters.stylus) { %>
- 'stylus',<% } if(filters.sass) { %>
- 'sass',<% } if(filters.less) { %>
- 'less',<% } %>
- 'imagemin'
- ]
- },
-
- // Test settings
- karma: {
- unit: {
- configFile: 'karma.conf.js',
- singleRun: true
- }
- },
-
- mochaTest: {
- options: {
- reporter: 'spec',
- require: 'mocha.conf.js',
- timeout: 5000 // set default mocha spec timeout
- },
- unit: {
- src: ['<%%= yeoman.server %>/**/*.spec.js']
- },
- integration: {
- src: ['<%%= yeoman.server %>/**/*.integration.js']
- }
- },
-
- mocha_istanbul: {
- unit: {
- options: {
- excludes: ['**/*.{spec,mock,integration}.js'],
- reporter: 'spec',
- require: ['mocha.conf.js'],
- mask: '**/*.spec.js',
- coverageFolder: 'coverage/server/unit'
- },
- src: '<%%= yeoman.server %>'
- },
- integration: {
- options: {
- excludes: ['**/*.{spec,mock,integration}.js'],
- reporter: 'spec',
- require: ['mocha.conf.js'],
- mask: '**/*.integration.js',
- coverageFolder: 'coverage/server/integration'
- },
- src: '<%%= yeoman.server %>'
- }
- },
-
- istanbul_check_coverage: {
- default: {
- options: {
- coverageFolder: 'coverage/**',
- check: {
- lines: 80,
- statements: 80,
- branches: 80,
- functions: 80
- }
- }
- }
- },
-
- protractor: {
- options: {
- configFile: 'protractor.conf.js'
- },
- chrome: {
- options: {
- args: {
- browser: 'chrome'
- }
- }
- }
- },
-
- env: {
- test: {
- NODE_ENV: 'test'
- },
- prod: {
- NODE_ENV: 'production'
- },
- all: localConfig
- },<% if(filters.jade) { %>
-
- // Compiles Jade to html
- jade: {
- compile: {
- options: {
- data: {
- debug: false
- }
- },
- files: [{
- expand: true,
- cwd: '<%%= yeoman.client %>',
- src: ['{app,components}/**/*.jade'],
- dest: '.tmp',
- ext: '.html'
- }]
- }
- },<% } %>
-
- // Compiles ES6 to JavaScript using Babel
- babel: {
- options: {
- sourceMap: true
- },<% if(filters.babel) { %>
- client: {
- files: [{
- expand: true,
- cwd: '<%%= yeoman.client %>',
- src: ['{app,components}/**/!(*.spec).js'],
- dest: '.tmp'
- }]
- },<% } %>
- server: {
- options: {
- plugins: [
- 'transform-class-properties',
- 'transform-runtime'
- ]
- },
- files: [{
- expand: true,
- cwd: '<%%= yeoman.server %>',
- src: [
- '**/*.js',
- '!config/local.env.sample.js'
- ],
- dest: '<%%= yeoman.dist %>/<%%= yeoman.server %>'
- }]
- }
- },<% if(filters.ts) { %>
-
- ts: {
- options: {
- sourceMap: true,
- failOnTypeErrors: false
- },
- client: {
- tsconfig: './tsconfig.client.json',
- outDir: '.tmp'
- },
- client_test: {
- tsconfig: './tsconfig.client.test.json',
- outDir: '.tmp/test'
- }
- },
-
- typings: {
- install: {}
- },<% } %><% if(filters.stylus) { %>
-
- // Compiles Stylus to CSS
- stylus: {
- server: {
- options: {
- "include css": true
- },
- files: {
- '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.styl'
- }
- }
- },<% } if(filters.sass) { %>
-
- // Compiles Sass to CSS
- sass: {
- server: {
- options: {
- compass: false
- },
- files: {
- '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.scss'
- }
- }
- },<% } if(filters.less) { %>
-
- // Compiles Less to CSS
- less: {
- options: {
- sourceMap: true,
- sourceMapFileInline: true
- },
- server: {
- files: {
- '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.less'
- }
- }
- },<% } %>
-
- injector: {
- options: {},
- // Inject application script files into index.html (doesn't include bower)
- scripts: {
- options: {
- transform: function(filePath) {
- var yoClient = grunt.config.get('yeoman.client');
- filePath = filePath.replace('/' + yoClient + '/', '');
- filePath = filePath.replace('/.tmp/', '');<% if(filters.ts) { %>
- filePath = filePath.replace('.ts', '.js');<% } %>
- return '';
- },
- sort: function(a, b) {
- var module = /\.module\.(js|ts)$/;
- var aMod = module.test(a);
- var bMod = module.test(b);
- // inject *.module.js first
- return (aMod === bMod) ? 0 : (aMod ? -1 : 1);
- },
- starttag: '',
- endtag: ''
- },
- files: {
- '<%%= yeoman.client %>/index.html': [
- [
- '<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).<%= scriptExt %>',<% if(filters.ts) { %>
- '<%%= yeoman.client %>/app/app.constant.js',<% } %>
- '!{.tmp,<%%= yeoman.client %>}/app/app.{js,ts}'
- ]
- ]
- }
- },<% if(filters.stylus) { %>
-
- // Inject component styl into app.styl
- stylus: {
- options: {
- transform: function(filePath) {
- var yoClient = grunt.config.get('yeoman.client');
- filePath = filePath.replace('/' + yoClient + '/app/', '');
- filePath = filePath.replace('/' + yoClient + '/components/', '../components/');
- return '@import \'' + filePath + '\';';
- },
- starttag: '/* inject:styl */',
- endtag: '/* endinject */'
- },
- files: {
- '<%%= yeoman.client %>/app/app.styl': [
- '<%%= yeoman.client %>/{app,components}/**/*.styl',
- '!<%%= yeoman.client %>/app/app.styl'
- ]
- }
- },<% } if(filters.sass) { %>
-
- // Inject component scss into app.scss
- sass: {
- options: {
- transform: function(filePath) {
- var yoClient = grunt.config.get('yeoman.client');
- filePath = filePath.replace('/' + yoClient + '/app/', '');
- filePath = filePath.replace('/' + yoClient + '/components/', '../components/');
- return '@import \'' + filePath + '\';';
- },
- starttag: '/* inject:scss */',
- endtag: '/* endinject */'
- },
- files: {
- '<%%= yeoman.client %>/app/app.scss': [
- '<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}',
- '!<%%= yeoman.client %>/app/app.{scss,sass}'
- ]
- }
- },<% } if(filters.less) { %>
-
- // Inject component less into app.less
- less: {
- options: {
- transform: function(filePath) {
- var yoClient = grunt.config.get('yeoman.client');
- filePath = filePath.replace('/' + yoClient + '/app/', '');
- filePath = filePath.replace('/' + yoClient + '/components/', '../components/');
- return '@import \'' + filePath + '\';';
- },
- starttag: '/* inject:less */',
- endtag: '/* endinject */'
- },
- files: {
- '<%%= yeoman.client %>/app/app.less': [
- '<%%= yeoman.client %>/{app,components}/**/*.less',
- '!<%%= yeoman.client %>/app/app.less'
- ]
- }
- },<% } %>
-
- // Inject component css into index.html
- css: {
- options: {
- transform: function(filePath) {
- var yoClient = grunt.config.get('yeoman.client');
- filePath = filePath.replace('/' + yoClient + '/', '');
- filePath = filePath.replace('/.tmp/', '');
- return '';
- },
- starttag: '',
- endtag: ''
- },
- files: {
- '<%%= yeoman.client %>/index.html': [
- '<%%= yeoman.client %>/{app,components}/**/*.css'
- ]
- }
- }
- },
- });
-
- // Used for delaying livereload until after server has restarted
- grunt.registerTask('wait', function() {
- grunt.log.ok('Waiting for server reload...');
-
- var done = this.async();
-
- setTimeout(function() {
- grunt.log.writeln('Done waiting!');
- done();
- }, 1500);
- });
-
- grunt.registerTask('express-keepalive', 'Keep grunt running', function() {
- this.async();
- });
-
- grunt.registerTask('serve', function(target) {
- if(target === 'dist') {
- return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']);
- }
-
- if(target === 'debug') {
- return grunt.task.run([
- 'clean:server',
- 'env:all',
- 'concurrent:pre',<% if(filters.ts) { %>
- 'typings',<% } %>
- 'concurrent:server',
- 'injector',
- 'wiredep:client',
- 'postcss',
- 'concurrent:debug'
- ]);
- }
-
- grunt.task.run([
- 'clean:server',
- 'env:all',
- 'concurrent:pre',<% if(filters.ts) { %>
- 'typings',<% } %>
- 'concurrent:server',
- 'injector',
- 'wiredep:client',
- 'postcss',
- 'express:dev',
- 'wait',
- 'open',
- 'watch'
- ]);
- });
-
- grunt.registerTask('server', function() {
- grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
- grunt.task.run(['serve']);
- });
-
- grunt.registerTask('test', function(target, option) {
- if(target === 'server') {
- return grunt.task.run([
- 'env:all',
- 'env:test',
- 'mochaTest:unit',
- 'mochaTest:integration'
- ]);
- } else if(target === 'client') {
- return grunt.task.run([
- 'clean:server',
- 'env:all',
- 'concurrent:pre',<% if(filters.ts) { %>
- 'ts:client',
- 'ts:client_test',
- 'typings',<% } %>
- 'concurrent:test',
- 'injector',
- 'postcss',
- 'wiredep:test',
- 'karma'
- ]);
- } else if(target === 'e2e') {
- if(option === 'prod') {
- return grunt.task.run([
- 'build',
- 'env:all',
- 'env:prod',
- 'express:prod',
- 'protractor'
- ]);
- } else {
- return grunt.task.run([
- 'clean:server',
- 'env:all',
- 'env:test',
- 'concurrent:pre',<% if(filters.ts) { %>
- 'typings',
- 'ts:client',
- 'ts:client_test',<% } %>
- 'concurrent:test',
- 'injector',
- 'wiredep:client',
- 'postcss',
- 'express:dev',
- 'protractor'
- ]);
- }
- } else if(target === 'coverage') {
- if(option === 'unit') {
- return grunt.task.run([
- 'env:all',
- 'env:test',
- 'mocha_istanbul:unit'
- ]);
- } else if(option === 'integration') {
- return grunt.task.run([
- 'env:all',
- 'env:test',
- 'mocha_istanbul:integration'
- ]);
- } else if(option === 'check') {
- return grunt.task.run([
- 'istanbul_check_coverage'
- ]);
- } else {
- return grunt.task.run([
- 'env:all',
- 'env:test',
- 'mocha_istanbul',
- 'istanbul_check_coverage'
- ]);
- }
- } else {
- grunt.task.run([
- 'test:server',
- 'test:client'
- ]);
- }
- });
-
- grunt.registerTask('build', [
- 'clean:dist',
- 'concurrent:pre',<% if(filters.ts) { %>
- 'typings',<% } %>
- 'concurrent:dist',
- 'injector',
- 'wiredep:client',
- 'useminPrepare',
- 'postcss',
- 'ngtemplates',
- 'concat',
- 'ngAnnotate',
- 'copy:dist',
- 'babel:server',
- 'cdnify',
- 'cssmin',
- 'uglify',
- 'filerev',
- 'usemin'
- ]);
-
- grunt.registerTask('default', [<% if(filters.ts) { %>
- 'newer:tslint',<% } %><% if(filters.babel) { %>
- 'newer:jshint',<% } %>
- 'test',
- 'build'
- ]);
-};
diff --git a/templates/app/README.md b/templates/app/README.md
index 02fbb93a6..68aeef789 100644
--- a/templates/app/README.md
+++ b/templates/app/README.md
@@ -7,27 +7,22 @@ This project was generated with the [Angular Full-Stack Generator](https://githu
### Prerequisites
- [Git](https://git-scm.com/)
-- [Node.js and npm](nodejs.org) Node ^4.2.3, npm ^2.14.7
-- [Bower](bower.io) (`npm install --global bower`)<% if(filters.sass) { %>
-- [Ruby](https://www.ruby-lang.org) and then `gem install sass`<% } if(filters.grunt) { %>
-- [Grunt](http://gruntjs.com/) (`npm install --global grunt-cli`)<% } if(filters.gulp) { %>
-- [Gulp](http://gulpjs.com/) (`npm install --global gulp`)<% } if(filters.mongoose) { %>
+- [Node.js and npm](nodejs.org) Node ^4.4.6, npm ^2.15.5
+- [Gulp](http://gulpjs.com/) (`npm install --global gulp`)<% if(filters.mongoose) { %>
- [MongoDB](https://www.mongodb.org/) - Keep a running daemon with `mongod`<% } if(filters.sequelize) { %>
- [SQLite](https://www.sqlite.org/quickstart.html)<% } %>
### Developing<% var i = 1; %>
-<%= i++ %>. Run `npm install` to install server dependencies.
-
-<%= i++ %>. Run `bower install` to install front-end dependencies.<% if(filters.mongoose) { %>
+<%= i++ %>. Run `npm install` to install server dependencies.<% if(filters.mongoose) { %>
<%= i++ %>. Run `mongod` in a separate shell to keep an instance of the MongoDB Daemon running<% } %>
-<%= i++ %>. Run <% if(filters.grunt) { %>`grunt serve`<% } if(filters.grunt && filters.gulp) { %> or <% } if(filters.gulp) { %>`gulp serve`<% } %> to start the development server. It should automatically open the client in your browser when ready.
+<%= i++ %>. Run `gulp serve` to start the development server. It should automatically open the client in your browser when ready.
## Build & development
-Run `grunt build` for building and `grunt serve` for preview.
+Run `gulp build` for building and `gulp serve` for preview.
## Testing
diff --git a/templates/app/_.babelrc b/templates/app/_.babelrc
index bde6914ee..87789669f 100644
--- a/templates/app/_.babelrc
+++ b/templates/app/_.babelrc
@@ -1,4 +1,9 @@
{
"presets": ["es2015"],
- "plugins": ["transform-class-properties"]
-}
\ No newline at end of file
+ "plugins": [
+ <%_ if(filters.flow) { -%>
+ "transform-flow-comments",
+ <%_ } -%>
+ "transform-class-properties"
+ ]
+}
diff --git a/templates/app/_.gitignore b/templates/app/_.gitignore
index 9fbd89aa0..c8041ad71 100644
--- a/templates/app/_.gitignore
+++ b/templates/app/_.gitignore
@@ -4,6 +4,7 @@ public
.sass-cache<% } %>
.idea
client/bower_components
+client/index.html
dist
/server/config/local.env.js
npm-debug.log
diff --git a/templates/app/_bower.json b/templates/app/_bower.json
deleted file mode 100644
index b2bcd90bd..000000000
--- a/templates/app/_bower.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "<%= lodash.slugify(lodash.humanize(appname)) %>",
- "version": "0.0.0",
- "dependencies": {
- "angular": "~1.5.0",
- "json3": "~3.3.1",
- "es5-shim": "~3.0.1",<% if(filters.bootstrap) { if (filters.sass) { %>
- "bootstrap-sass-official": "~3.1.1",<% } %>
- "bootstrap": "~3.1.1",<% if(filters.oauth) { %>
- "bootstrap-social": "~4.9.1",<% }} %>
- "angular-resource": "~1.5.0",
- "angular-cookies": "~1.5.0",
- "angular-sanitize": "~1.5.0",<% if (filters.ngroute) { %>
- "angular-route": "~1.5.0",<% } if (filters.uibootstrap) { %>
- "angular-bootstrap": "~1.1.2",<% } %>
- "font-awesome": ">=4.1.0",
- "lodash": "~2.4.1"<% if(filters.socketio) { %>,
- "angular-socket-io": "~0.7.0"<% } if (filters.uirouter) { %>,
- "angular-ui-router": "~0.2.15"<% } if (filters.auth) { %>,
- "angular-validation-match": "~1.5.2"<% } %>
- },
- "devDependencies": {
- "angular-mocks": "~1.5.0"
- }
-}
diff --git a/templates/app/_package.json b/templates/app/_package.json
index 5f65f65ab..da24c6ced 100644
--- a/templates/app/_package.json
+++ b/templates/app/_package.json
@@ -1,8 +1,26 @@
{
"name": "<%= lodash.slugify(lodash.humanize(appname)) %>",
"version": "0.0.0",
- "main": "server/app.js",
+ "main": "server/index.js",
"dependencies": {
+ <%# CLIENT %>
+ "angular": "~1.5.5",<% if(filters.bootstrap) { if(filters.sass) { %>
+ "bootstrap-sass": "~3.1.1",<% } %>
+ "bootstrap": "~3.1.1",<% if(filters.oauth) { %>
+ "bootstrap-social": "~4.9.1",<% }} %>
+ "angular-animate": "~1.5.5",
+ "angular-aria": "~1.5.5",
+ "angular-resource": "~1.5.5",
+ "angular-cookies": "~1.5.5",
+ "angular-sanitize": "~1.5.5",<% if(filters.ngroute) { %>
+ "angular-route": "~1.5.5",<% } if(filters.uibootstrap) { %>
+ "angular-ui-bootstrap": "~1.1.2",<% } %>
+ "font-awesome": ">=4.1.0",<% if(filters.socketio) { %>
+ "angular-socket-io": "~0.7.0",<% } if(filters.uirouter) { %>
+ "angular-ui-router": "~0.2.15",<% } if(filters.auth) { %>
+ "angular-validation-match": "~1.5.2",<% } %>
+ <%# END CLIENT %>
+ "core-js": "^2.2.1",
"express": "^4.13.3",
"morgan": "~1.7.0",
"body-parser": "^1.13.3",
@@ -12,37 +30,52 @@
"errorhandler": "^1.4.2",
"compression": "^1.5.2",
"composable-middleware": "^0.3.0",
+ "fast-json-patch": "^1.0.0",
"lodash": "^4.6.1",
"lusca": "^1.3.0",
"babel-runtime": "^6.6.1",
- "babel-polyfill": "^6.7.2",<% if (filters.jade) { %>
- "jade": "^1.11.0",<% } %><% if (filters.html) { %>
- "ejs": "^2.3.3",<% } %><% if (filters.mongoose) { %>
+ "babel-polyfill": "^6.7.2",<% if(filters.jade) { %>
+ "jade": "^1.11.0",<% } %><% if(filters.html) { %>
+ "ejs": "^2.3.3",<% } %><% if(filters.mongoose) { %>
"mongoose": "^4.1.2",
"bluebird": "^3.3.3",
- "connect-mongo": "^1.2.1",<% } %><% if (filters.sequelize) { %>
+ "connect-mongo": "^1.2.1",<% } %><% if(filters.sequelize) { %>
"sequelize": "^3.5.1",
"sqlite3": "~3.1.1",
- "express-sequelize-session": "0.4.0",<% } %><% if (filters.auth) { %>
+ "express-sequelize-session": "0.4.0",<% } %><% if(filters.auth) { %>
"jsonwebtoken": "^7.0.0",
"express-jwt": "^3.0.0",
"passport": "~0.3.0",
- "passport-local": "^1.0.0",<% } %><% if (filters.facebookAuth) { %>
- "passport-facebook": "^2.0.0",<% } %><% if (filters.twitterAuth) { %>
- "passport-twitter": "^1.0.3",<% } %><% if (filters.googleAuth) { %>
- "passport-google-oauth20": "^1.0.0",<% } %><% if (filters.socketio) { %>
+ "passport-local": "^1.0.0",<% } %><% if(filters.facebookAuth) { %>
+ "passport-facebook": "^2.0.0",<% } %><% if(filters.twitterAuth) { %>
+ "passport-twitter": "^1.0.3",<% } %><% if(filters.googleAuth) { %>
+ "passport-google-oauth20": "^1.0.0",<% } %><% if(filters.socketio) { %>
"socket.io": "^1.3.5",
"socket.io-client": "^1.3.5",
"socketio-jwt": "^4.2.0",<% } %>
- "serve-favicon": "^2.3.0"
+ "serve-favicon": "^2.3.0",
+ "sprint-js": "~0.1.0"
},
"devDependencies": {
+ <%# CLIENT %>
+ "angular-mocks": "~1.5.5",<% if(filters.stylus) { %>
+ "bootstrap-styl": "^5.0.5",
+ "font-awesome-stylus": "^4.6.2",<% } %>
+ <%# END CLIENT %>
"autoprefixer": "^6.0.0",
"babel-core": "^6.6.5",
+ "babel-eslint": "^6.0.4",
"babel-register": "^6.6.5",
+ "browser-sync": "^2.8.0",
+ "bs-fullscreen-message": "^1.0.0",
+ <%_ if(filters.flow) { -%>
+ "flow-bin": "^0.27.0",
+ "babel-plugin-syntax-flow": "^6.8.0",
+ "babel-plugin-transform-flow-comments": "^6.8.0",<% } %>
"babel-plugin-transform-class-properties": "^6.6.0",
"babel-plugin-transform-runtime": "^6.6.0",
- "babel-preset-es2015": "^6.6.0",<% if(filters.gulp) { %>
+ "babel-preset-es2015": "^6.6.0",
+ "eslint": "^2.12.0",
"del": "^2.0.2",
"gulp": "^3.9.1",
"gulp-add-src": "^0.2.0",
@@ -51,24 +84,21 @@
"gulp-babel": "^6.1.2",<% if(filters.ts) { %>
"gulp-typescript": "~2.13.0",
"gulp-typings": "^1.3.6",
- "gulp-tslint": "^5.0.0",<% } %>
+ "gulp-tslint": "^5.0.0",
+ "ts-helpers": "1.1.1",<% } %>
"gulp-cache": "^0.4.2",
"gulp-concat": "^2.6.0",
"gulp-env": "^0.4.0",
+ "gulp-eslint": "^2.0.0",
"gulp-filter": "^4.0.0",
"gulp-imagemin": "^2.2.1",
"gulp-inject": "^4.0.0",
"gulp-istanbul": "~0.10.3",
"gulp-istanbul-enforcer": "^1.0.3",
- "gulp-jscs": "^3.0.2",
- "gulp-jshint": "^2.0.0",
- "jshint": "2.9.2",
- "gulp-livereload": "^3.8.0",
"gulp-load-plugins": "^1.0.0-rc.1",
"gulp-clean-css": "^2.0.6",
"gulp-mocha": "^2.1.3",
"gulp-ng-annotate": "^2.0.0",
- "gulp-ng-constant": "^1.1.0",
"gulp-node-inspector": "^0.1.0",
"gulp-plumber": "^1.0.1",
"gulp-protractor": "^2.1.0",
@@ -97,52 +127,40 @@
"nodemon": "^1.3.7",
"run-sequence": "^1.1.0",
"lazypipe": "^1.0.1",
- "wiredep": "^2.2.2",<% } /*end gulp*/ if(filters.grunt) { %>
- "grunt": "^0.4.5",
- "grunt-wiredep": "^2.0.0",
- "grunt-concurrent": "^2.0.1",
- "grunt-contrib-clean": "^1.0.0",
- "grunt-contrib-concat": "^1.0.0",
- "grunt-contrib-copy": "^1.0.0",
- "grunt-contrib-cssmin": "^1.0.0",
- "grunt-contrib-imagemin": "^1.0.0",
- "grunt-contrib-jshint": "^1.0.0",
- "grunt-contrib-uglify": "^1.0.0",
- "grunt-contrib-watch": "^1.0.0",<% if (filters.jade) { %>
- "grunt-contrib-jade": "^1.0.0",<% } %><% if (filters.less) { %>
- "grunt-contrib-less": "^1.2.0",<% } %>
- "grunt-babel": "~6.0.0",<% if(filters.ts) { %>
- "grunt-ts": "^5.3.2",
- "grunt-typings": "~0.1.4",
- "grunt-tslint": "~3.1.0",<% } %>
- "grunt-google-cdn": "~0.4.0",
- "grunt-jscs": "^2.1.0",
- "grunt-newer": "^1.1.1",
- "grunt-ng-annotate": "^2.0.1",
- "grunt-ng-constant": "^2.0.1",
- "grunt-filerev": "^2.3.1",
- "grunt-usemin": "^3.0.0",
- "grunt-env": "~0.4.1",
- "grunt-node-inspector": "^0.4.1",
- "grunt-nodemon": "^0.4.0",
- "grunt-angular-templates": "^1.0.3",
- "grunt-dom-munger": "^3.4.0",
- "grunt-protractor-runner": "^3.1.0",
- "grunt-injector": "~0.6.0 ",
- "grunt-karma": "^1.0.0",
- "grunt-build-control": "^0.7.0",<% if(filters.sass) { %>
- "grunt-sass": "^1.1.0",<% } %><% if(filters.stylus) { %>
- "grunt-contrib-stylus": "^1.2.0",<% } %>
- "jit-grunt": "~0.10.0",
- "grunt-express-server": "^0.5.1",
- "grunt-postcss": "~0.8.0",
- "grunt-open": "~0.2.3",
- "time-grunt": "^1.2.1",
- "grunt-mocha-test": "~0.12.7",
- "grunt-mocha-istanbul": "^4.0.2",<% } /*end grunt*/ %>
+ <%# WEBPACK %>
+ "webpack": "^1.12.14",
+ "webpack-dev-middleware": "^1.5.1",
+ "webpack-dev-server": "~1.14.0",
+ "webpack-stream": "^3.2.0",
+ "extract-text-webpack-plugin": "^1.0.1",
+ "html-webpack-plugin": "^2.16.0",
+ "html-webpack-harddisk-plugin": "~0.0.2",
+ <%_ if(filters.jade) { _%>
+ "pug-html-loader": "^1.0.8",<% } %>
+ "awesome-typescript-loader": "0.17.0",
+ "ng-annotate-loader": "~0.1.0",
+ "babel-loader": "^6.2.4",
+ "css-loader": "^0.23.1",
+ "file-loader": "^0.8.4",
+ "imports-loader": "^0.6.5",
+ "isparta-instrumenter-loader": "^1.0.0",
+ "isparta-loader": "^2.0.0",
+ "istanbul-instrumenter-loader": "^0.2.0",
+ "null-loader": "^0.1.1",
+ "postcss-loader": "^0.9.1",
+ "raw-loader": "^0.5.1",
+ "style-loader": "^0.13.0",
+ <%_ if(filters.sass) { _%>
+ "sass-loader": "^3.1.1",<% } %>
+ <%_ if(filters.less) { _%>
+ "less-loader": "^2.2.3",<% } %>
+ <%_ if(filters.stylus) { _%>
+ "stylus-loader": "^2.1.1",<% } %>
+ "tiny-lr": "^0.2.1",
+ "karma-webpack": "^1.7.0",
+ <%# END WEBPACK %>
+ "through2": "^2.0.1",
"open": "~0.0.4",
- "jshint-stylish": "^2.2.0",
- "connect-livereload": "^0.5.3",
"istanbul": "~0.4.1",
"chai": "^3.2.0",
"sinon": "^1.16.1",
@@ -151,39 +169,42 @@
"karma": "~0.13.3",
"karma-firefox-launcher": "^1.0.0",
"karma-script-launcher": "^1.0.0",
+ "karma-coverage": "^0.5.3",
"karma-chrome-launcher": "^1.0.1",
- "karma-jade-preprocessor": "0.0.11",
- "karma-phantomjs-launcher": "~1.0.0",<% if (filters.jade) { %>
- "karma-ng-jade2js-preprocessor": "^0.2.0",<% } %>
- "karma-ng-html2js-preprocessor": "^1.0.0",
+ "karma-phantomjs-launcher": "~1.0.0",
"karma-spec-reporter": "~0.0.20",
+ "karma-sourcemap-loader": "~0.3.7",
"sinon-chai": "^2.8.0",
- "mocha": "^2.2.5",<% if (filters.mocha) { %>
+ "mocha": "^2.2.5",<% if(filters.mocha) { %>
"karma-mocha": "^1.0.1",
- "karma-chai-plugins": "~0.7.0",<% } if (filters.jasmine) { %>
+ "karma-chai-plugins": "~0.7.0",<% } if(filters.jasmine) { %>
"jasmine-core": "^2.3.4",
"karma-jasmine": "^1.0.2",
- "jasmine-spec-reporter": "^2.4.0",<% } if(filters.babel) { %>
- "karma-babel-preprocessor": "^6.0.1",<% } %>
+ "jasmine-spec-reporter": "^2.4.0",<% } %>
"phantomjs-prebuilt": "^2.1.4",
"proxyquire": "^1.0.1",
+ "strip-ansi": "^3.0.1",
"supertest": "^1.1.0"<% if(filters.ts) { %>,
"tslint": "^3.5.0",
"typings": "^0.8.1"<% } %>
},
"engines": {
- "node": "^4.4.0",
- "npm": "^2.14.20"
+ "node": "^4.4.6",
+ "npm": "^2.15.5"
},
- "scripts": {<% if(filters.gulp) { %>
- "test": "gulp test",<% } else { %>
- "test": "grunt test",<% } %>
- <%_ if(filters.ts) { _%>
- "postinstall": "./node_modules/.bin/typings install",<% } %>
- <%_ if(semver.satisfies(npmVersion, '>= 3')) { _%>
+ "scripts": {
+ "test": "gulp test",
+ <%_ if(filters.ts) { -%>
+ "postinstall": "./node_modules/.bin/typings install",
+ <%_ } -%>
+ <%_ if(filters.flow) { -%>
+ "flow": "flow",
+ <%_ } -%>
+ <%_ if(semver.satisfies(npmVersion, '>= 3')) { -%>
"update-webdriver": "node node_modules/protractor/bin/webdriver-manager update",
- <%_ } else { _%>
- "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update",<% } %>
+ <%_ } else { -%>
+ "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update",
+ <%_ } -%>
"start": "node server"
},
"private": true
diff --git a/templates/app/client/.eslintrc(babel) b/templates/app/client/.eslintrc(babel)
new file mode 100644
index 000000000..b12322f67
--- /dev/null
+++ b/templates/app/client/.eslintrc(babel)
@@ -0,0 +1,7 @@
+{
+ "extends": "../.eslintrc",
+ "env": {
+ "browser": true,
+ "commonjs": true
+ }
+}
diff --git a/templates/app/client/.jshintrc(babel) b/templates/app/client/.jshintrc(babel)
deleted file mode 100644
index 9d0d57476..000000000
--- a/templates/app/client/.jshintrc(babel)
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "node": true,
- "browser": true,
- "esnext": true,
- "bitwise": true,
- "camelcase": true,
- "curly": true,
- "eqeqeq": true,
- "immed": true,
- "latedef": true,
- "newcap": true,
- "noarg": true,
- "quotmark": "single",
- "undef": true,
- "unused": true,
- "strict": true,
- "trailing": true,
- "smarttabs": true,
- "ignoreDelimiters": [
- { "start": "start-non-standard", "end": "end-non-standard" }
- ],
- "globals": {
- "jQuery": true,
- "angular": true,
- "console": true,
- "$": true,
- "_": true,
- "moment": true,<% if (filters.jasmine) { %>
- "jasmine": true,<% } %>
- "describe": true,
- "beforeEach": true,
- "module": true,
- "inject": true,
- "it": true,
- "expect": true,
- "browser": true,
- "element": true,
- "by": true
- }
-}
diff --git a/templates/app/client/__index.html b/templates/app/client/__index.html
new file mode 100644
index 000000000..29826783d
--- /dev/null
+++ b/templates/app/client/__index.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+ Angular Full-Stack Generator
+
+
+
+
+
+
+
+
+
+
+
+ <% if (filters.ngroute) { %><% } %><% if (filters.uirouter) { %><% } %>
+
+
+
diff --git a/templates/app/client/app/account(auth)/account.js b/templates/app/client/app/account(auth)/account.routes.js
similarity index 53%
rename from templates/app/client/app/account(auth)/account.js
rename to templates/app/client/app/account(auth)/account.routes.js
index 4266116fa..d15b803c5 100644
--- a/templates/app/client/app/account(auth)/account.js
+++ b/templates/app/client/app/account(auth)/account.routes.js
@@ -1,81 +1,72 @@
'use strict';
-angular.module('<%= scriptAppName %>')
- <% if (filters.ngroute) { %>.config(function($routeProvider) {
- $routeProvider
- .when('/login', {
- templateUrl: 'app/account/login/login.html',
+<%_ if (filters.uirouter) { _%>
+export default function routes($stateProvider) {
+ 'ngInject';
+ $stateProvider
+ .state('login', {
+ url: '/login',
+ template: require('./login/login.<%= templateExt %>'),
controller: 'LoginController',
controllerAs: 'vm'
})
- .when('/logout', {
- name: 'logout',
- referrer: '/',
+ .state('logout', {
+ url: '/logout?referrer',
+ referrer: 'main',
template: '',
- controller: function($location, $route, Auth) {
- var referrer = $route.current.params.referrer ||
- $route.current.referrer ||
- '/';
+ controller: function($state, Auth) {
+ 'ngInject';
+ var referrer = $state.params.referrer
+ || $state.current.referrer
+ || 'main';
Auth.logout();
- $location.path(referrer);
+ $state.go(referrer);
}
})
- .when('/signup', {
- templateUrl: 'app/account/signup/signup.html',
+ .state('signup', {
+ url: '/signup',
+ template: require('./signup/signup.<%= templateExt %>'),
controller: 'SignupController',
controllerAs: 'vm'
})
- .when('/settings', {
- templateUrl: 'app/account/settings/settings.html',
+ .state('settings', {
+ url: '/settings',
+ template: require('./settings/settings.<%= templateExt %>'),
controller: 'SettingsController',
controllerAs: 'vm',
authenticate: true
});
- })
- .run(function($rootScope) {
- $rootScope.$on('$routeChangeStart', function(event, next, current) {
- if (next.name === 'logout' && current && current.originalPath && !current.authenticate) {
- next.referrer = current.originalPath;
- }
- });
- });<% } %><% if (filters.uirouter) { %>.config(function($stateProvider) {
- $stateProvider
- .state('login', {
- url: '/login',
- templateUrl: 'app/account/login/login.html',
+}<% } %>
+<%_ if (filters.ngroute) { _%>
+export default function routes($routeProvider) {
+ 'ngInject';
+ $routeProvider
+ .when('/login', {
+ template: require('./login/login.<%= templateExt %>'),
controller: 'LoginController',
controllerAs: 'vm'
})
- .state('logout', {
- url: '/logout?referrer',
- referrer: 'main',
+ .when('/logout', {
+ name: 'logout',
+ referrer: '/',
template: '',
- controller: function($state, Auth) {
- var referrer = $state.params.referrer ||
- $state.current.referrer ||
- 'main';
+ controller: function($location, $route, Auth) {
+ var referrer = $route.current.params.referrer ||
+ $route.current.referrer ||
+ '/';
Auth.logout();
- $state.go(referrer);
+ $location.path(referrer);
}
})
- .state('signup', {
- url: '/signup',
- templateUrl: 'app/account/signup/signup.html',
+ .when('/signup', {
+ template: require('./signup/signup.<%= templateExt %>'),
controller: 'SignupController',
controllerAs: 'vm'
})
- .state('settings', {
- url: '/settings',
- templateUrl: 'app/account/settings/settings.html',
+ .when('/settings', {
+ template: require('./settings/settings.<%= templateExt %>'),
controller: 'SettingsController',
controllerAs: 'vm',
authenticate: true
});
- })
- .run(function($rootScope) {
- $rootScope.$on('$stateChangeStart', function(event, next, nextParams, current) {
- if (next.name === 'logout' && current && current.name && !current.authenticate) {
- next.referrer = current.name;
- }
- });
- });<% } %>
+}<% } %>
diff --git a/templates/app/client/app/account(auth)/index.js b/templates/app/client/app/account(auth)/index.js
new file mode 100644
index 000000000..48ade7142
--- /dev/null
+++ b/templates/app/client/app/account(auth)/index.js
@@ -0,0 +1,44 @@
+'use strict';
+import angular from 'angular';
+<%_ if (filters.ngroute) { _%>
+const ngRoute = require('angular-route'); <% } %>
+<%_ if (filters.uirouter) { _%>
+import uiRouter from 'angular-ui-router';<% } %>
+
+import routing from './account.routes';
+import login from './login';
+import settings from './settings';
+import signup from './signup';
+<%_ if(filters.oauth) { _%>
+import oauthButtons from '../../components/oauth-buttons';<% } %>
+
+export default angular.module('<%= scriptAppName %>.account', [
+ <%_ if (filters.ngroute) { _%>
+ ngRoute,<% } %>
+ <%_ if (filters.uirouter) { _%>
+ uiRouter,<% } %>
+ login,
+ settings,
+ signup<% if(filters.oauth) { %>,
+ oauthButtons<% } %>
+])
+ .config(routing)
+ <%_ if (filters.ngroute) { _%>
+ .run(function($rootScope) {
+ 'ngInject';
+ $rootScope.$on('$routeChangeStart', function(event, next, current) {
+ if (next.name === 'logout' && current && current.originalPath && !current.authenticate) {
+ next.referrer = current.originalPath;
+ }
+ });
+ })<% } %>
+ <%_ if (filters.uirouter) { _%>
+ .run(function($rootScope) {
+ 'ngInject';
+ $rootScope.$on('$stateChangeStart', function(event, next, nextParams, current) {
+ if (next.name === 'logout' && current && current.name && !current.authenticate) {
+ next.referrer = current.name;
+ }
+ });
+ })<% } %>
+ .name;
diff --git a/templates/app/client/app/account(auth)/login/index.js b/templates/app/client/app/account(auth)/login/index.js
new file mode 100644
index 000000000..187b43be5
--- /dev/null
+++ b/templates/app/client/app/account(auth)/login/index.js
@@ -0,0 +1,6 @@
+'use strict';
+import LoginController from './login.controller';
+
+export default angular.module('<%= scriptAppName %>.login', [])
+ .controller('LoginController', LoginController)
+ .name;
diff --git a/templates/app/client/app/account(auth)/login/login(html).html b/templates/app/client/app/account(auth)/login/login(html).html
index 76382615f..2f72c7268 100644
--- a/templates/app/client/app/account(auth)/login/login(html).html
+++ b/templates/app/client/app/account(auth)/login/login(html).html
@@ -28,7 +28,7 @@ Login
Please enter a valid email.
- {{ vm.errors.other }}
+ {{ vm.errors.login }}
diff --git a/templates/app/client/app/account(auth)/login/login(jade).jade b/templates/app/client/app/account(auth)/login/login(jade).jade
index c46f0e5eb..a463419d4 100644
--- a/templates/app/client/app/account(auth)/login/login(jade).jade
+++ b/templates/app/client/app/account(auth)/login/login(jade).jade
@@ -27,7 +27,7 @@
.form-group.has-error
p.help-block(ng-show='form.email.$error.required && form.password.$error.required && vm.submitted')
| Please enter your email and password.
- p.help-block {{ vm.errors.other }}
+ p.help-block {{ vm.errors.login }}
div
button.btn.btn-inverse.btn-lg.btn-login(type='submit')
diff --git a/templates/app/client/app/account(auth)/login/login.controller.js b/templates/app/client/app/account(auth)/login/login.controller.js
index d53fcbe20..a6dd073a4 100644
--- a/templates/app/client/app/account(auth)/login/login.controller.js
+++ b/templates/app/client/app/account(auth)/login/login.controller.js
@@ -1,13 +1,40 @@
'use strict';
+// @flow
+<%_ if(filters.flow) { -%>
+type User = {
+ name: string;
+ email: string;
+ password: string;
+};
+<%_ } -%>
+<%_ if(filters.ts) { -%>
+interface User {
+ name: string;
+ email: string;
+ password: string;
+}
+<%_ } -%>
-class LoginController {
- constructor(Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>) {
- this.user = {};
- this.errors = {};
- this.submitted = false;
+export default class LoginController {
+ user: User = {
+ name: '',
+ email: '',
+ password: ''
+ };
+ errors = {login: undefined};
+ submitted = false;
+ Auth;
+ <%_ if(filters.ngroute) { -%>
+ $location;
+ <%_ } if(filters.uirouter) { -%>
+ $state;<% } %>
- this.Auth = Auth;<% if (filters.ngroute) { %>
- this.$location = $location;<% } if (filters.uirouter) { %>
+ /*@ngInject*/
+ constructor(Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>) {
+ this.Auth = Auth;
+ <%_ if(filters.ngroute) { -%>
+ this.$location = $location;
+ <%_ } if(filters.uirouter) { -%>
this.$state = $state;<% } %>
}
@@ -24,11 +51,8 @@ class LoginController {
<% if (filters.ngroute) { %>this.$location.path('/');<% } %><% if (filters.uirouter) { %>this.$state.go('main');<% } %>
})
.catch(err => {
- this.errors.other = err.message;
+ this.errors.login = err.message;
});
}
}
}
-
-angular.module('<%= scriptAppName %>')
- .controller('LoginController', LoginController);
diff --git a/templates/app/client/app/account(auth)/settings/index.js b/templates/app/client/app/account(auth)/settings/index.js
new file mode 100644
index 000000000..823caa530
--- /dev/null
+++ b/templates/app/client/app/account(auth)/settings/index.js
@@ -0,0 +1,6 @@
+'use strict';
+import SettingsController from './settings.controller';
+
+export default angular.module('<%= scriptAppName %>.settings', [])
+ .controller('SettingsController', SettingsController)
+ .name;
diff --git a/templates/app/client/app/account(auth)/settings/settings.controller.js b/templates/app/client/app/account(auth)/settings/settings.controller.js
index 58c89e0ff..d6316b7a5 100644
--- a/templates/app/client/app/account(auth)/settings/settings.controller.js
+++ b/templates/app/client/app/account(auth)/settings/settings.controller.js
@@ -1,9 +1,32 @@
'use strict';
+// @flow
+<%_ if(filters.flow) { -%>
+type User = {
+ oldPassword: string;
+ newPassword: string;
+ confirmPassword: string;
+};
+<%_ } -%>
+<%_ if(filters.ts) { -%>
+interface User {
+ oldPassword: string;
+ newPassword: string;
+ confirmPassword: string;
+}
+<%_ } -%>
-class SettingsController {
- errors = {};
+export default class SettingsController {
+ user: User = {
+ oldPassword: '',
+ newPassword: '',
+ confirmPassword: ''
+ };
+ errors = {other: undefined};
+ message = '';
submitted = false;
+ Auth;
+ /*@ngInject*/
constructor(Auth) {
this.Auth = Auth;
}
@@ -11,7 +34,7 @@ class SettingsController {
changePassword(form) {
this.submitted = true;
- if (form.$valid) {
+ if(form.$valid) {
this.Auth.changePassword(this.user.oldPassword, this.user.newPassword)
.then(() => {
this.message = 'Password successfully changed.';
@@ -24,6 +47,3 @@ class SettingsController {
}
}
}
-
-angular.module('<%= scriptAppName %>')
- .controller('SettingsController', SettingsController);
diff --git a/templates/app/client/app/account(auth)/signup/index.js b/templates/app/client/app/account(auth)/signup/index.js
new file mode 100644
index 000000000..43b4842b7
--- /dev/null
+++ b/templates/app/client/app/account(auth)/signup/index.js
@@ -0,0 +1,7 @@
+'use strict';
+
+import SignupController from './signup.controller';
+
+export default angular.module('<%= scriptAppName %>.signup', [])
+ .controller('SignupController', SignupController)
+ .name;
diff --git a/templates/app/client/app/account(auth)/signup/signup.controller.js b/templates/app/client/app/account(auth)/signup/signup.controller.js
index a6f83fb58..196684c24 100644
--- a/templates/app/client/app/account(auth)/signup/signup.controller.js
+++ b/templates/app/client/app/account(auth)/signup/signup.controller.js
@@ -1,43 +1,69 @@
'use strict';
+// @flow
+<%_ if(filters.flow) { -%>
+type User = {
+ name: string;
+ email: string;
+ password: string;
+};
+<%_ } -%>
+<%_ if(filters.ts) { -%>
+interface User {
+ name: string;
+ email: string;
+ password: string;
+}
+<%_ } -%>
-class SignupController {
- //start-non-standard
- user = {};
+export default class SignupController {
+ user: User = {
+ name: '',
+ email: '',
+ password: ''
+ };
errors = {};
submitted = false;
- //end-non-standard
+ Auth;
+ <%_ if(filters.ngroute) { -%>
+ $location;
+ <%_ } if(filters.uirouter) { -%>
+ $state;<% } %>
+ /*@ngInject*/
constructor(Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>) {
- this.Auth = Auth;<% if (filters.ngroute) { %>
- this.$location = $location;<% } if (filters.uirouter) { %>
+ this.Auth = Auth;
+ <%_ if(filters.ngroute) { -%>
+ this.$location = $location;
+ <%_ } if(filters.uirouter) { -%>
this.$state = $state;<% } %>
}
register(form) {
this.submitted = true;
- if (form.$valid) {
- this.Auth.createUser({
+ if(form.$valid) {
+ return this.Auth.createUser({
name: this.user.name,
email: this.user.email,
password: this.user.password
})
.then(() => {
// Account created, redirect to home
- <% if (filters.ngroute) { %>this.$location.path('/');<% } %><% if (filters.uirouter) { %>this.$state.go('main');<% } %>
+ <% if(filters.ngroute) { %>this.$location.path('/');<% } -%>
+ <% if(filters.uirouter) { %>this.$state.go('main');<% } -%>
})
.catch(err => {
err = err.data;
this.errors = {};
-<% if (filters.mongooseModels) { %>
+ <%_ if(filters.mongooseModels) { -%>
// Update validity of form fields that match the mongoose errors
angular.forEach(err.errors, (error, field) => {
form[field].$setValidity('mongoose', false);
this.errors[field] = error.message;
- });<% }
-if (filters.sequelizeModels) { %>
+ });<% } %>
+ <%_ if(filters.sequelizeModels) { -%>
// Update validity of form fields that match the sequelize errors
- if (err.name) {
+ if(err.name) {
angular.forEach(err.fields, field => {
form[field].$setValidity('mongoose', false);
this.errors[field] = err.message;
@@ -47,6 +73,3 @@ if (filters.sequelizeModels) { %>
}
}
}
-
-angular.module('<%= scriptAppName %>')
- .controller('SignupController', SignupController);
diff --git a/templates/app/client/app/admin(auth)/admin.controller.js b/templates/app/client/app/admin(auth)/admin.controller.js
index b56309871..afb2e557b 100644
--- a/templates/app/client/app/admin(auth)/admin.controller.js
+++ b/templates/app/client/app/admin(auth)/admin.controller.js
@@ -1,12 +1,11 @@
'use strict';
-(function() {
-
-class AdminController {
+export default class AdminController {
<%_ if(filters.ts || filters.flow) { _%>
users: Object[];
<%_ } _%>
+ /*@ngInject*/
constructor(User) {
// Use the User $resource to fetch all users
this.users = User.query();
@@ -17,8 +16,3 @@ class AdminController {
this.users.splice(this.users.indexOf(user), 1);
}
}
-
-angular.module('<%= scriptAppName %>.admin')
- .controller('AdminController', AdminController);
-
-})();
diff --git a/templates/app/client/app/admin(auth)/admin.module.js b/templates/app/client/app/admin(auth)/admin.module.js
deleted file mode 100644
index da769ae91..000000000
--- a/templates/app/client/app/admin(auth)/admin.module.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>.admin', [
- '<%= scriptAppName %>.auth'<% if (filters.ngroute) { %>,
- 'ngRoute'<% } if (filters.uirouter) { %>,
- 'ui.router'<% } %>
-]);
diff --git a/templates/app/client/app/admin(auth)/admin.router.js b/templates/app/client/app/admin(auth)/admin.router.js
deleted file mode 100644
index cc3d9e9f9..000000000
--- a/templates/app/client/app/admin(auth)/admin.router.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>.admin')
- <% if (filters.ngroute) { %>.config(function($routeProvider) {
- $routeProvider
- .when('/admin', {
- templateUrl: 'app/admin/admin.html',
- controller: 'AdminController',
- controllerAs: 'admin',
- authenticate: 'admin'
- });
- });<% } if (filters.uirouter) { %>.config(function($stateProvider) {
- $stateProvider
- .state('admin', {
- url: '/admin',
- templateUrl: 'app/admin/admin.html',
- controller: 'AdminController',
- controllerAs: 'admin',
- authenticate: 'admin'
- });
- });<% } %>
diff --git a/templates/app/client/app/admin(auth)/admin.routes.js b/templates/app/client/app/admin(auth)/admin.routes.js
new file mode 100644
index 000000000..24114892e
--- /dev/null
+++ b/templates/app/client/app/admin(auth)/admin.routes.js
@@ -0,0 +1,25 @@
+'use strict';
+
+<%_ if (filters.ngroute) { _%>
+export default function routes($routeProvider) {
+ 'ngInject';
+ $routeProvider
+ .when('/admin', {
+ template: require('./admin.<%= templateExt %>'),
+ controller: 'AdminController',
+ controllerAs: 'admin',
+ authenticate: 'admin'
+ });
+};<% } %>
+<%_ if (filters.uirouter) { _%>
+export default function routes($stateProvider) {
+ 'ngInject';
+ $stateProvider
+ .state('admin', {
+ url: '/admin',
+ template: require('./admin.<%= templateExt %>'),
+ controller: 'AdminController',
+ controllerAs: 'admin',
+ authenticate: 'admin'
+ });
+};<% } %>
diff --git a/templates/app/client/app/admin(auth)/index.js b/templates/app/client/app/admin(auth)/index.js
new file mode 100644
index 000000000..d78165412
--- /dev/null
+++ b/templates/app/client/app/admin(auth)/index.js
@@ -0,0 +1,12 @@
+'use strict';
+import routes from './admin.routes';
+import AdminController from './admin.controller';
+
+export default angular.module('<%= scriptAppName %>.admin', [
+ '<%= scriptAppName %>.auth'<% if (filters.ngroute) { %>,
+ 'ngRoute'<% } if (filters.uirouter) { %>,
+ 'ui.router'<% } %>
+])
+ .config(routes)
+ .controller('AdminController', AdminController)
+ .name;
diff --git a/templates/app/client/app/app(css).css b/templates/app/client/app/app(css).css
index 2dbd1e8c5..d1b63a10f 100644
--- a/templates/app/client/app/app(css).css
+++ b/templates/app/client/app/app(css).css
@@ -1,16 +1,23 @@
-<% if (filters.bootstrap) { %>
+<%_ if (filters.bootstrap) { -%>
+@import '~bootstrap/dist/css/bootstrap.css';
+<%_ if(filters.oauth) { -%>
+@import '~bootstrap-social/bootstrap-social.css';
+<%_ } -%>
/**
* Bootstrap Fonts
*/
@font-face {
font-family: 'Glyphicons Halflings';
- src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot');
- src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
-}<% } %>
+ src: url('/assets/fonts/bootstrap/glyphicons-halflings-regular.eot');
+ src: url('/assets/fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
+ url('/assets/fonts/bootstrap/glyphicons-halflings-regular.woff') format('woff'),
+ url('/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
+ url('/assets/fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+
+<%_ } -%>
+@import '~font-awesome/css/font-awesome.css';
/**
*Font Awesome Fonts
@@ -18,11 +25,11 @@
@font-face {
font-family: 'FontAwesome';
- src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0');
- src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
+ src: url('/assets/fonts/font-awesome/fontawesome-webfont.eot?v=4.1.0');
+ src: url('/assets/fonts/font-awesome/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
+ url('/assets/fonts/font-awesome/fontawesome-webfont.woff?v=4.1.0') format('woff'),
+ url('/assets/fonts/font-awesome/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
+ url('/assets/fonts/font-awesome/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -44,3 +51,12 @@
max-width: 730px;
}
}<% } %>
+
+/* Component styles are injected through gulp */
+/* inject:css */
+@import 'admin/admin.css';
+@import 'main/main.css';
+@import '../components/footer/footer.css';
+@import '../components/modal/modal.css';
+@import '../components/oauth-buttons/oauth-buttons.css';
+/* endinject */
diff --git a/templates/app/client/app/app(less).less b/templates/app/client/app/app(less).less
index 44268cf9f..191118fbb 100644
--- a/templates/app/client/app/app(less).less
+++ b/templates/app/client/app/app(less).less
@@ -1,9 +1,9 @@
-<% if (filters.bootstrap) { %>@import '../bower_components/bootstrap/less/bootstrap.less';
-<% if(filters.oauth) { %>@import '../bower_components/bootstrap-social/bootstrap-social.less';
-<% }} %>@import '../bower_components/font-awesome/less/font-awesome.less';
+<% if(filters.bootstrap) { %>@import '~bootstrap/less/bootstrap.less';
+<% if(filters.oauth) { %>@import '~bootstrap-social/bootstrap-social.less';
+<% }} %>@import '~font-awesome/less/font-awesome.less';
-<% if (filters.bootstrap) { %>@icon-font-path: '../bower_components/bootstrap/fonts/';<% } %>
-@fa-font-path: '../bower_components/font-awesome/fonts';
+<% if(filters.bootstrap) { %>@icon-font-path: '/assets/fonts/bootstrap/';<% } %>
+@fa-font-path: '/assets/fonts/font-awesome/';
/**
* App-wide Styles
diff --git a/templates/app/client/app/app(sass).scss b/templates/app/client/app/app(sass).scss
index 0191121c1..e0956fff7 100644
--- a/templates/app/client/app/app(sass).scss
+++ b/templates/app/client/app/app(sass).scss
@@ -1,8 +1,13 @@
-<% if (filters.bootstrap) { %>$icon-font-path: "../bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";
-@import '../bower_components/bootstrap-sass-official/vendor/assets/stylesheets/bootstrap';
-<% if(filters.oauth) { %>@import '../bower_components/bootstrap-social/bootstrap-social.scss';
-<% }} %>$fa-font-path: "../bower_components/font-awesome/fonts";
-@import '../bower_components/font-awesome/scss/font-awesome';
+<%_ if (filters.bootstrap) { _%>
+$icon-font-path: '/assets/fonts/bootstrap/';
+@import '~bootstrap-sass/vendor/assets/stylesheets/bootstrap';
+<%_ if(filters.oauth) { _%>
+@import '~bootstrap-social/bootstrap-social.scss';
+<% }} _%>
+$fa-font-path: '/assets/fonts/font-awesome/';
+@import '~font-awesome/scss/font-awesome';
+
+// @import url(http://fonts.googleapis.com/css?family=Open+Sans:300,400,600,900|Roboto:300,500,700,900);
/**
* App-wide Styles
@@ -22,7 +27,7 @@
}
}
<% } %>
-// Component styles are injected through grunt
+// Component styles are injected through gulp
/* inject:scss */
@import 'admin/admin.scss';
@import 'main/main.scss';
diff --git a/templates/app/client/app/app(stylus).styl b/templates/app/client/app/app(stylus).styl
index ca1778ffd..a38c5b193 100644
--- a/templates/app/client/app/app(stylus).styl
+++ b/templates/app/client/app/app(stylus).styl
@@ -1,32 +1,11 @@
-@import "../bower_components/font-awesome/css/font-awesome.css"
-<% if (filters.bootstrap) { %>@import "../bower_components/bootstrap/dist/css/bootstrap.css"
-<% if (filters.oauth) { %>@import "../bower_components/bootstrap-social/bootstrap-social.css"
-<% } %>
-//
-// Bootstrap Fonts
-//
-
-@font-face
- font-family: 'Glyphicons Halflings'
- src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot')
- src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
- url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
-<% } %>
-//
-// Font Awesome Fonts
-//
+$fa-font-path = '../assets/fonts/font-awesome/'
+@import '~font-awesome-stylus/stylus/index'
-@font-face
- font-family: 'FontAwesome'
- src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0')
- src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
- url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
- font-weight: normal
- font-style: normal
+<%_ if (filters.bootstrap) { %>
+$icon-font-path = '../assets/fonts/bootstrap/'
+@import '~bootstrap-styl/bootstrap/index';
+<% if (filters.oauth) { %>@import '~bootstrap-social/bootstrap-social.css'
+<% } %><% } %>
//
// App-wide Styles
@@ -43,7 +22,7 @@
.container
max-width 730px
<% } %>
-// Component styles are injected through grunt
+// Component styles are injected through gulp
/* inject:styl */
@import "admin/admin"
@import "main/main"
diff --git a/templates/app/client/app/app.config.js b/templates/app/client/app/app.config.js
new file mode 100644
index 000000000..3b3ea607b
--- /dev/null
+++ b/templates/app/client/app/app.config.js
@@ -0,0 +1,15 @@
+'use strict';
+
+export function routeConfig(<% if (filters.ngroute) { %>$routeProvider<% } if (filters.uirouter) { %>$urlRouterProvider<% } %>, $locationProvider) {
+ 'ngInject';
+ <%_ if(filters.ngroute) { _%>
+ $routeProvider
+ .otherwise({
+ redirectTo: '/'
+ });<% } %>
+ <%_ if(filters.uirouter) { _%>
+ $urlRouterProvider
+ .otherwise('/');<% } %>
+
+ $locationProvider.html5Mode(true);
+}
diff --git a/templates/app/client/app/app.constants.js b/templates/app/client/app/app.constants.js
new file mode 100644
index 000000000..885cf21a1
--- /dev/null
+++ b/templates/app/client/app/app.constants.js
@@ -0,0 +1,6 @@
+'use strict';
+import angular from 'angular';
+
+export default angular.module('<%= scriptAppName %>.constants', [])
+ .constant('appConfig', require('../../server/config/environment/shared'))
+ .name;
diff --git a/templates/app/client/app/app.js b/templates/app/client/app/app.js
index a9d9f2b09..7ccaaf89c 100644
--- a/templates/app/client/app/app.js
+++ b/templates/app/client/app/app.js
@@ -1,13 +1,84 @@
'use strict';
+import angular from 'angular';
+// import ngAnimate from 'angular-animate';
+import ngCookies from 'angular-cookies';
+import ngResource from 'angular-resource';
+import ngSanitize from 'angular-sanitize';
+<%_ if(filters.socketio) { _%>
+import 'angular-socket-io';<% } %>
+<%_ if(filters.ngroute) { _%>
+const ngRoute = require('angular-route');<% } %>
+<%_ if(filters.uirouter) { _%>
+import uiRouter from 'angular-ui-router';<% } %>
+<%_ if(filters.uibootstrap) { _%>
+import uiBootstrap from 'angular-ui-bootstrap';<% } %>
+// import ngMessages from 'angular-messages';
+<%_ if(filters.auth) { _%>
+//import ngValidationMatch from 'angular-validation-match';<% } %>
-angular.module('<%= scriptAppName %>', [<%- angularModules %>])
- .config(function(<% if (filters.ngroute) { %>$routeProvider<% } if (filters.uirouter) { %>$urlRouterProvider<% } %>, $locationProvider) {<% if (filters.ngroute) { %>
- $routeProvider
- .otherwise({
- redirectTo: '/'
- });<% } if (filters.uirouter) { %>
- $urlRouterProvider
- .otherwise('/');<% } %>
- $locationProvider.html5Mode(true);
+import {routeConfig} from './app.config';
+
+<%_ if(filters.auth) { _%>
+import _Auth from '../components/auth/auth.module';
+import account from './account';
+import admin from './admin';<% } %>
+import navbar from '../components/navbar/navbar.component';
+import footer from '../components/footer/footer.component';
+import main from './main/main.component';
+import constants from './app.constants';
+import util from '../components/util/util.module';
+<%_ if(filters.socketio) { _%>
+import socket from '../components/socket/socket.service';<% } %>
+
+
+import './app.<%= styleExt %>';
+
+angular.module('<%= scriptAppName %>', [
+ // ngAnimate,
+ ngCookies,
+ ngResource,
+ ngSanitize,
+ <%_ if(filters.socketio) { %>
+ 'btford.socket-io',<% } %>
+ <%_ if(filters.ngroute) { %>
+ ngRoute,<% } _%>
+ <%_ if(filters.uirouter) { %>
+ uiRouter,<% } _%>
+ <%_ if(filters.uibootstrap) { %>
+ uiBootstrap,<% } %>
+ // ngMessages,
+ <%_ if(filters.auth) { %>
+ // ngValidationMatch,
+ _Auth,
+ account,
+ admin,<% } _%>
+ navbar,
+ footer,
+ main,
+ constants,
+ <%_ if(filters.socketio) { _%>
+ socket,<% } %>
+ util
+])
+ .config(routeConfig)
+ <%_ if(filters.auth) { _%>
+ .run(function($rootScope, $location, Auth) {
+ 'ngInject';
+ // Redirect to login if route requires auth and you're not logged in
+ $rootScope.$on('$stateChangeStart', function(event, next) {
+ Auth.isLoggedIn(function(loggedIn) {
+ if(next.authenticate && !loggedIn) {
+ $location.path('/login');
+ }
+ });
+ });
+ })<% } %>;
+
+angular
+ .element(document)
+ .ready(() => {
+ angular.bootstrap(document, ['<%= scriptAppName %>'], {
+ strictDi: true
+ });
});
diff --git a/templates/app/client/app/main/main.component.js b/templates/app/client/app/main/main.component.js
new file mode 100644
index 000000000..a1dc2479a
--- /dev/null
+++ b/templates/app/client/app/main/main.component.js
@@ -0,0 +1,58 @@
+import angular from 'angular';
+<%_ if(filters.ngroute) { _%>
+const ngRoute = require('angular-route');<% } _%>
+<%_ if(filters.uirouter) { _%>
+import uiRouter from 'angular-ui-router';<% } _%>
+
+import routing from './main.routes';
+
+export class MainController {
+ $http;
+ <%_ if(filters.socketio) { -%>
+ socket;<% } %>
+ awesomeThings = [];
+ <%_ if(filters.models) { -%>
+ newThing = '';<% } %>
+
+ /*@ngInject*/
+ constructor($http<% if(filters.socketio) { %>, $scope, socket<% } %>) {
+ this.$http = $http;
+ <%_ if(filters.socketio) { -%>
+ this.socket = socket;
+
+ $scope.$on('$destroy', function() {
+ socket.unsyncUpdates('thing');
+ });<% } %>
+ }
+
+ $onInit() {
+ this.$http.get('/api/things').then(response => {
+ this.awesomeThings = response.data;<% if (filters.socketio) { %>
+ this.socket.syncUpdates('thing', this.awesomeThings);<% } %>
+ });
+ }<% if (filters.models) { %>
+
+ addThing() {
+ if (this.newThing) {
+ this.$http.post('/api/things', { name: this.newThing });
+ this.newThing = '';
+ }
+ }
+
+ deleteThing(thing) {
+ this.$http.delete('/api/things/' + thing._id);
+ }<% } %>
+}
+
+export default angular.module('<%= scriptAppName %>.main', [
+ <%_ if(filters.ngroute) { _%>
+ ngRoute<% } _%>
+ <%_ if(filters.uirouter) { _%>
+ uiRouter<% } _%>
+])
+ .config(routing)
+ .component('main', {
+ template: require('./main.<%= templateExt %>'),
+ controller: MainController
+ })
+ .name;
diff --git a/templates/app/client/app/main/main.controller.spec.js b/templates/app/client/app/main/main.component.spec.js
similarity index 75%
rename from templates/app/client/app/main/main.controller.spec.js
rename to templates/app/client/app/main/main.component.spec.js
index 67982faa0..ac10b1c54 100644
--- a/templates/app/client/app/main/main.controller.spec.js
+++ b/templates/app/client/app/main/main.component.spec.js
@@ -1,11 +1,15 @@
'use strict';
-describe('Component: mainComponent', function() {
+import main from './main.component';
+import {MainController} from './main.component';
- // load the controller's module
- beforeEach(module('<%= scriptAppName %>'));<% if (filters.uirouter) {%>
- beforeEach(module('stateMock'));<% } %><% if (filters.socketio) {%>
- beforeEach(module('socketMock'));<% } %>
+describe('Component: MainComponent', function() {
+
+ beforeEach(angular.mock.module(main));
+ <%_ if (filters.uirouter) { _%>
+ beforeEach(angular.mock.module('stateMock'));<% } _%>
+ <%_ if (filters.socketio) { _%>
+ beforeEach(angular.mock.module('socketMock'));<% } %>
var scope;
var mainComponent;<% if (filters.uirouter) {%>
diff --git a/templates/app/client/app/main/main.controller.js b/templates/app/client/app/main/main.controller.js
deleted file mode 100644
index 02f408164..000000000
--- a/templates/app/client/app/main/main.controller.js
+++ /dev/null
@@ -1,42 +0,0 @@
-'use strict';
-
-(function() {
-
-class MainController {
-
- constructor($http<% if (filters.socketio) { %>, $scope, socket<% } %>) {
- this.$http = $http;<% if (filters.socketio) { %>
- this.socket = socket;<% } %>
- this.awesomeThings = [];<% if (filters.socketio) { %>
-
- $scope.$on('$destroy', function() {
- socket.unsyncUpdates('thing');
- });<% } %>
- }
-
- $onInit() {
- this.$http.get('/api/things').then(response => {
- this.awesomeThings = response.data;<% if (filters.socketio) { %>
- this.socket.syncUpdates('thing', this.awesomeThings);<% } %>
- });
- }<% if (filters.models) { %>
-
- addThing() {
- if (this.newThing) {
- this.$http.post('/api/things', { name: this.newThing });
- this.newThing = '';
- }
- }
-
- deleteThing(thing) {
- this.$http.delete('/api/things/' + thing._id);
- }<% } %>
-}
-
-angular.module('<%= scriptAppName %>')
- .component('main', {
- templateUrl: 'app/main/main.html',
- controller: MainController
- });
-
-})();
diff --git a/templates/app/client/app/main/main.js b/templates/app/client/app/main/main.js
deleted file mode 100644
index 9d5f59887..000000000
--- a/templates/app/client/app/main/main.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>')
- <% if (filters.ngroute) { %>.config(function($routeProvider) {
- $routeProvider
- .when('/', {
- template: '
'
- });
- });<% } %><% if (filters.uirouter) { %>.config(function($stateProvider) {
- $stateProvider
- .state('main', {
- url: '/',
- template: '
'
- });
- });<% } %>
diff --git a/templates/app/client/app/main/main.routes.js b/templates/app/client/app/main/main.routes.js
new file mode 100644
index 000000000..95c7968a9
--- /dev/null
+++ b/templates/app/client/app/main/main.routes.js
@@ -0,0 +1,19 @@
+'use strict';
+
+<%_ if(filters.ngroute) { _%>
+export default function routes($routeProvider) {
+ 'ngInject';
+ $routeProvider
+ .when('/', {
+ template: '
'
+ });
+};<% } %>
+<%_ if(filters.uirouter) { _%>
+export default function routes($stateProvider) {
+ 'ngInject';
+ $stateProvider
+ .state('main', {
+ url: '/',
+ template: '
'
+ });
+};<% } %>
diff --git a/templates/app/client/components/auth(auth)/auth.module.js b/templates/app/client/components/auth(auth)/auth.module.js
index 60ce6e321..1859bcd00 100644
--- a/templates/app/client/components/auth(auth)/auth.module.js
+++ b/templates/app/client/components/auth(auth)/auth.module.js
@@ -1,12 +1,32 @@
'use strict';
+import angular from 'angular';
+import constants from '../../app/app.constants';
+import util from '../util/util.module';
+import ngCookies from 'angular-cookies';
+import {authInterceptor} from './interceptor.service';
+import {routerDecorator} from './router.decorator';
+import {AuthService} from './auth.service';
+import {UserResource} from './user.service';
+<%_ if (filters.ngroute) { _%>
+const ngRoute = require('angular-route');<% } %>
+<%_ if (filters.uirouter) { _%>
+import uiRouter from 'angular-ui-router';<% } %>
-angular.module('<%= scriptAppName %>.auth', [
- '<%= scriptAppName %>.constants',
- '<%= scriptAppName %>.util',
- 'ngCookies'<% if (filters.ngroute) { %>,
- 'ngRoute'<% } if (filters.uirouter) { %>,
- 'ui.router'<% } %>
+function addInterceptor($httpProvider) {
+ 'ngInject';
+ $httpProvider.interceptors.push('authInterceptor');
+}
+
+export default angular.module('<%= scriptAppName %>.auth', [
+ constants,
+ util,
+ ngCookies<% if(filters.ngroute) { %>,
+ ngRoute<% } if(filters.uirouter) { %>,
+ uiRouter<% } %>
])
- .config(function($httpProvider) {
- $httpProvider.interceptors.push('authInterceptor');
- });
+ .factory('authInterceptor', authInterceptor)
+ .run(routerDecorator)
+ .factory('Auth', AuthService)
+ .factory('User', UserResource)
+ .config(['$httpProvider', addInterceptor])
+ .name;
diff --git a/templates/app/client/components/auth(auth)/auth.service.js b/templates/app/client/components/auth(auth)/auth.service.js
index 3a75c5a9a..87a0adb2f 100644
--- a/templates/app/client/components/auth(auth)/auth.service.js
+++ b/templates/app/client/components/auth(auth)/auth.service.js
@@ -1,13 +1,28 @@
'use strict';
+// @flow
+class User {
+ _id: string = '';
+ name: string = '';
+ email: string = '';
+ role: string = '';
+ $promise = undefined;
+}
-(function() {
-
-function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
+export function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
+ 'ngInject';
var safeCb = Util.safeCb;
- var currentUser = {};
+ var currentUser: User = new User();
var userRoles = appConfig.userRoles || [];
+ /**
+ * Check if userRole is >= role
+ * @param {String} userRole - role of current user
+ * @param {String} role - role to check against
+ */
+ var hasRole = function(userRole, role) {
+ return userRoles.indexOf(userRole) >= userRoles.indexOf(role);
+ };
- if ($cookies.get('token') && $location.path() !== '/logout') {
+ if($cookies.get('token') && $location.path() !== '/logout') {
currentUser = User.get();
}
@@ -17,14 +32,11 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
* Authenticate user and save token
*
* @param {Object} user - login info
- * @param {Function} callback - optional, function(error, user)
+ * @param {Function} callback - function(error, user)
* @return {Promise}
*/
- login({email, password}, callback: Function) {
- return $http.post('/auth/local', {
- email: email,
- password: password
- })
+ login({email, password}, callback?: Function) {
+ return $http.post('/auth/local', { email, password })
.then(res => {
$cookies.put('token', res.data.token);
currentUser = User.get();
@@ -46,17 +58,17 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
*/
logout() {
$cookies.remove('token');
- currentUser = {};
+ currentUser = new User();
},
/**
* Create a new user
*
* @param {Object} user - user info
- * @param {Function} callback - optional, function(error, user)
+ * @param {Function} callback - function(error, user)
* @return {Promise}
*/
- createUser(user, callback) {
+ createUser(user, callback?: Function) {
return User.save(user,
function(data) {
$cookies.put('token', data.token);
@@ -74,14 +86,11 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
*
* @param {String} oldPassword
* @param {String} newPassword
- * @param {Function} callback - optional, function(error, user)
+ * @param {Function} callback - function(error, user)
* @return {Promise}
*/
- changePassword(oldPassword, newPassword, callback) {
- return User.changePassword({ id: currentUser._id }, {
- oldPassword: oldPassword,
- newPassword: newPassword
- }, function() {
+ changePassword(oldPassword, newPassword, callback?: Function) {
+ return User.changePassword({ id: currentUser._id }, { oldPassword, newPassword }, function() {
return safeCb(callback)(null);
}, function(err) {
return safeCb(callback)(err);
@@ -90,18 +99,15 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
/**
* Gets all available info on a user
- * (synchronous|asynchronous)
*
- * @param {Function|*} callback - optional, funciton(user)
- * @return {Object|Promise}
+ * @param {Function} [callback] - function(user)
+ * @return {Promise}
*/
- getCurrentUser(callback) {
- if (arguments.length === 0) {
- return currentUser;
- }
+ getCurrentUser(callback?: Function) {
+ var value = currentUser.hasOwnProperty('$promise')
+ ? currentUser.$promise
+ : currentUser;
- var value = (currentUser.hasOwnProperty('$promise')) ?
- currentUser.$promise : currentUser;
return $q.when(value)
.then(user => {
safeCb(callback)(user);
@@ -113,18 +119,22 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
},
/**
- * Check if a user is logged in
- * (synchronous|asynchronous)
+ * Gets all available info on a user
*
- * @param {Function|*} callback - optional, function(is)
- * @return {Bool|Promise}
+ * @return {Object}
*/
- isLoggedIn(callback) {
- if (arguments.length === 0) {
- return currentUser.hasOwnProperty('role');
- }
+ getCurrentUserSync() {
+ return currentUser;
+ },
- return Auth.getCurrentUser(null)
+ /**
+ * Check if a user is logged in
+ *
+ * @param {Function} [callback] - function(is)
+ * @return {Promise}
+ */
+ isLoggedIn(callback?: Function) {
+ return Auth.getCurrentUser(undefined)
.then(user => {
var is = user.hasOwnProperty('role');
safeCb(callback)(is);
@@ -132,32 +142,44 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
});
},
+ /**
+ * Check if a user is logged in
+ *
+ * @return {Bool}
+ */
+ isLoggedInSync() {
+ return currentUser.hasOwnProperty('role');
+ },
+
/**
* Check if a user has a specified role or higher
- * (synchronous|asynchronous)
*
* @param {String} role - the role to check against
- * @param {Function|*} callback - optional, function(has)
- * @return {Bool|Promise}
+ * @param {Function} [callback] - function(has)
+ * @return {Promise}
*/
- hasRole(role, callback) {
- var hasRole = function(r, h) {
- return userRoles.indexOf(r) >= userRoles.indexOf(h);
- };
-
- if (arguments.length < 2) {
- return hasRole(currentUser.role, role);
- }
-
- return Auth.getCurrentUser(null)
+ hasRole(role, callback?: Function) {
+ return Auth.getCurrentUser(undefined)
.then(user => {
- var has = (user.hasOwnProperty('role')) ?
- hasRole(user.role, role) : false;
+ var has = user.hasOwnProperty('role')
+ ? hasRole(user.role, role)
+ : false;
+
safeCb(callback)(has);
return has;
});
},
+ /**
+ * Check if a user has a specified role or higher
+ *
+ * @param {String} role - the role to check against
+ * @return {Bool}
+ */
+ hasRoleSync(role) {
+ return hasRole(currentUser.role, role);
+ },
+
/**
* Check if a user is an admin
* (synchronous|asynchronous)
@@ -170,6 +192,15 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
.apply(Auth, [].concat.apply(['admin'], arguments));
},
+ /**
+ * Check if a user is an admin
+ *
+ * @return {Bool}
+ */
+ isAdminSync() {
+ return Auth.hasRoleSync('admin');
+ },
+
/**
* Get auth token
*
@@ -182,8 +213,3 @@ function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
return Auth;
}
-
-angular.module('<%= scriptAppName %>.auth')
- .factory('Auth', AuthService);
-
-})();
diff --git a/templates/app/client/components/auth(auth)/interceptor.service.js b/templates/app/client/components/auth(auth)/interceptor.service.js
index 72c3441b0..e868b8924 100644
--- a/templates/app/client/components/auth(auth)/interceptor.service.js
+++ b/templates/app/client/components/auth(auth)/interceptor.service.js
@@ -1,10 +1,10 @@
'use strict';
-(function() {
-
-function authInterceptor($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>, Util) {
- <% if (filters.uirouter) { %>var state;
- <% } %>return {
+export function authInterceptor($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>, Util) {
+ 'ngInject';
+ <%_ if (filters.uirouter) { _%>
+ var state;<% } %>
+ return {
// Add authorization token to headers
request(config) {
config.headers = config.headers || {};
@@ -17,7 +17,10 @@ function authInterceptor($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $
// Intercept 401s and redirect you to login
responseError(response) {
if (response.status === 401) {
- <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>(state || (state = $injector.get('$state'))).go('login');<% } %>
+ <%_ if (filters.ngroute) { _%>
+ $location.path('/login');<% } %>
+ <%_ if (filters.uirouter) { _%>
+ (state || (state = $injector.get('$state'))).go('login');<% } %>
// remove any stale tokens
$cookies.remove('token');
}
@@ -25,8 +28,3 @@ function authInterceptor($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $
}
};
}
-
-angular.module('<%= scriptAppName %>.auth')
- .factory('authInterceptor', authInterceptor);
-
-})();
diff --git a/templates/app/client/components/auth(auth)/router.decorator.js b/templates/app/client/components/auth(auth)/router.decorator.js
index 5b8fb0e6a..ea205ec7d 100644
--- a/templates/app/client/components/auth(auth)/router.decorator.js
+++ b/templates/app/client/components/auth(auth)/router.decorator.js
@@ -1,39 +1,40 @@
'use strict';
+import * as _ from 'lodash';
-(function() {
+export function routerDecorator($rootScope<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $state<% } %>, Auth) {
+ 'ngInject';
+ // Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
+ $rootScope.$on(<% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, function(event, next) {
+ if(!next.authenticate) {
+ return;
+ }
-angular.module('<%= scriptAppName %>.auth')
- .run(function($rootScope<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $state<% } %>, Auth) {
- // Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
- $rootScope.$on(<% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, function(event, next) {
- if (!next.authenticate) {
- return;
- }
+ if(typeof next.authenticate === 'string') {
+ Auth.hasRole(next.authenticate).then(has => {
+ if(has) {
+ return;
+ }
- if (typeof next.authenticate === 'string') {
- Auth.hasRole(next.authenticate, _.noop).then(has => {
- if (has) {
- return;
- }
-
- event.preventDefault();
- return Auth.isLoggedIn(_.noop).then(is => {<% if (filters.ngroute) { %>
- $location.path(is ? '/' : '/login');<% } if (filters.uirouter) { %>
- $state.go(is ? 'main' : 'login');<% } %>
- });
+ event.preventDefault();
+ return Auth.isLoggedIn().then(is => {
+ <%_ if(filters.ngroute) { _%>
+ $location.path(is ? '/' : '/login');<% } %>
+ <%_ if(filters.uirouter) { _%>
+ $state.go(is ? 'main' : 'login');<% } %>
});
- } else {
- Auth.isLoggedIn(_.noop).then(is => {
- if (is) {
- return;
- }
+ });
+ } else {
+ Auth.isLoggedIn().then(is => {
+ if(is) {
+ return;
+ }
- event.preventDefault();<% if (filters.ngroute) { %>
- $location.path('/');<% } if (filters.uirouter) { %>
- $state.go('main');<% } %>
- });
- }
- });
+ event.preventDefault();
+ <%_ if(filters.ngroute) { _%>
+ $location.path('/login');<% } %>
+ <%_ if(filters.uirouter) { _%>
+ $state.go('login');<% } %>
+ });
+ }
});
-
-})();
+};
diff --git a/templates/app/client/components/auth(auth)/user.service.js b/templates/app/client/components/auth(auth)/user.service.js
index 63f4040d0..a458a89a2 100644
--- a/templates/app/client/components/auth(auth)/user.service.js
+++ b/templates/app/client/components/auth(auth)/user.service.js
@@ -1,8 +1,7 @@
'use strict';
-(function() {
-
-function UserResource($resource) {
+export function UserResource($resource) {
+ 'ngInject';
return $resource('/api/users/:id/:controller', {
id: '@_id'
}, {
@@ -20,8 +19,3 @@ function UserResource($resource) {
}
});
}
-
-angular.module('<%= scriptAppName %>.auth')
- .factory('User', UserResource);
-
-})();
diff --git a/templates/app/client/components/footer/footer.component.js b/templates/app/client/components/footer/footer.component.js
new file mode 100644
index 000000000..88b9312a7
--- /dev/null
+++ b/templates/app/client/components/footer/footer.component.js
@@ -0,0 +1,10 @@
+import angular from 'angular';
+
+export class FooterComponent {}
+
+export default angular.module('directives.footer', [])
+ .component('footer', {
+ template: require('./footer.<%= templateExt %>'),
+ controller: FooterComponent
+ })
+ .name;
diff --git a/templates/app/client/components/footer/footer.directive.js b/templates/app/client/components/footer/footer.directive.js
deleted file mode 100644
index 9ed31900c..000000000
--- a/templates/app/client/components/footer/footer.directive.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict';
-
-angular.module('<%= scriptAppName %>')
- .directive('footer', function() {
- return {
- templateUrl: 'components/footer/footer.html',
- restrict: 'E',
- link: function(scope, element) {
- element.addClass('footer');
- }
- };
- });
diff --git a/templates/app/client/components/modal(uibootstrap)/modal.service.js b/templates/app/client/components/modal(uibootstrap)/modal.service.js
index d7807e6b5..91d94894c 100644
--- a/templates/app/client/components/modal(uibootstrap)/modal.service.js
+++ b/templates/app/client/components/modal(uibootstrap)/modal.service.js
@@ -1,73 +1,76 @@
'use strict';
-angular.module('<%= scriptAppName %>')
- .factory('Modal', function($rootScope, $uibModal) {
- /**
- * Opens a modal
- * @param {Object} scope - an object to be merged with modal's scope
- * @param {String} modalClass - (optional) class(es) to be applied to the modal
- * @return {Object} - the instance $uibModal.open() returns
- */
- function openModal(scope = {}, modalClass = 'modal-default') {
- var modalScope = $rootScope.$new();
+export function Modal($rootScope, $uibModal) {
+ /**
+ * Opens a modal
+ * @param {Object} scope - an object to be merged with modal's scope
+ * @param {String} modalClass - (optional) class(es) to be applied to the modal
+ * @return {Object} - the instance $uibModal.open() returns
+ */
+ function openModal(scope = {}, modalClass = 'modal-default') {
+ var modalScope = $rootScope.$new();
- angular.extend(modalScope, scope);
+ angular.extend(modalScope, scope);
- return $uibModal.open({
- templateUrl: 'components/modal/modal.html',
- windowClass: modalClass,
- scope: modalScope
- });
- }
+ return $uibModal.open({
+ template: require('./modal.<%= templateExt %>'),
+ windowClass: modalClass,
+ scope: modalScope
+ });
+ }
- // Public API here
- return {
+ // Public API here
+ return {
- /* Confirmation modals */
- confirm: {
+ /* Confirmation modals */
+ confirm: {
+ /**
+ * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
+ * @param {Function} del - callback, ran when delete is confirmed
+ * @return {Function} - the function to open the modal (ex. myModalFn)
+ */
+ delete(del = angular.noop) {
/**
- * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
- * @param {Function} del - callback, ran when delete is confirmed
- * @return {Function} - the function to open the modal (ex. myModalFn)
+ * Open a delete confirmation modal
+ * @param {String} name - name or info to show on modal
+ * @param {All} - any additional args are passed straight to del callback
*/
- delete(del = angular.noop) {
- /**
- * Open a delete confirmation modal
- * @param {String} name - name or info to show on modal
- * @param {All} - any additional args are passed straight to del callback
- */
- return function() {
- var args = Array.prototype.slice.call(arguments),
- name = args.shift(),
- deleteModal;
+ return function() {
+ var args = Array.prototype.slice.call(arguments),
+ name = args.shift(),
+ deleteModal;
- deleteModal = openModal({
- modal: {
- dismissable: true,
- title: 'Confirm Delete',
- html: '
Are you sure you want to delete ' + name + ' ?
',
- buttons: [{
- classes: 'btn-danger',
- text: 'Delete',
- click: function(e) {
- deleteModal.close(e);
- }
- }, {
- classes: 'btn-default',
- text: 'Cancel',
- click: function(e) {
- deleteModal.dismiss(e);
- }
- }]
- }
- }, 'modal-danger');
+ deleteModal = openModal({
+ modal: {
+ dismissable: true,
+ title: 'Confirm Delete',
+ html: '
Are you sure you want to delete ' + name + ' ?
',
+ buttons: [{
+ classes: 'btn-danger',
+ text: 'Delete',
+ click: function(e) {
+ deleteModal.close(e);
+ }
+ }, {
+ classes: 'btn-default',
+ text: 'Cancel',
+ click: function(e) {
+ deleteModal.dismiss(e);
+ }
+ }]
+ }
+ }, 'modal-danger');
- deleteModal.result.then(function(event) {
- del.apply(event, args);
- });
- };
- }
+ deleteModal.result.then(function(event) {
+ del.apply(event, args);
+ });
+ };
}
- };
- });
+ }
+ };
+}
+
+export default angular.module('<%= scriptAppName %>')
+ .factory('Modal', Modal)
+ .name;
diff --git a/templates/app/client/components/navbar/navbar(html).html b/templates/app/client/components/navbar/navbar(html).html
index 4b6692d36..79d7b5a78 100644
--- a/templates/app/client/components/navbar/navbar(html).html
+++ b/templates/app/client/components/navbar/navbar(html).html
@@ -1,7 +1,7 @@
-
+
-
+
<% 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..cd32fb74f 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')
+ div#navbar-main.navbar-collapse.collapse(uib-collapse='$ctrl.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)}'<% } %>)
+ 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")}'<% } %>)
+ 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 53%
rename from templates/app/client/components/navbar/navbar.controller.js
rename to templates/app/client/components/navbar/navbar.component.js
index 12ae0b351..6a6d37282 100644
--- a/templates/app/client/components/navbar/navbar.controller.js
+++ b/templates/app/client/components/navbar/navbar.component.js
@@ -1,24 +1,30 @@
'use strict';
-class NavbarController {
- //start-non-standard
+export class NavbarComponent {
menu = [{
'title': 'Home',
<% if (filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %>
}];
-
+ <%_ if(!filters.uirouter) { -%>
+ $location;
+ <%_ } -%>
+ <%_ if (filters.auth) { -%>
+ isLoggedIn: Function;
+ isAdmin: Function;
+ getCurrentUser: Function;
+ <%_ } -%>
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;
<%_ } _%>
<%_ if (filters.auth) { _%>
- this.isLoggedIn = Auth.isLoggedIn;
- this.isAdmin = Auth.isAdmin;
- this.getCurrentUser = Auth.getCurrentUser;
+ this.isLoggedIn = Auth.isLoggedInSync;
+ this.isAdmin = Auth.isAdminSync;
+ this.getCurrentUser = Auth.getCurrentUserSync;
<%_ } _%>
}<% } %>
<%_ if(!filters.uirouter) { _%>
@@ -28,5 +34,9 @@ class NavbarController {
}<% } %>
}
-angular.module('<%= scriptAppName %>')
- .controller('NavbarController', NavbarController);
+export default angular.module('directives.navbar', [])
+ .component('navbar', {
+ template: require('./navbar.<%= templateExt %>'),
+ 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..8ce33c85e
--- /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.<%= templateExt %>'),
+ 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..4728acc89 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,12 @@
'use strict';
+const $ = require('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 +25,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 +41,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.mock.js b/templates/app/client/components/socket(socketio)/socket.mock.js
index ba09c1d35..fdf9e5a57 100644
--- a/templates/app/client/components/socket(socketio)/socket.mock.js
+++ b/templates/app/client/components/socket(socketio)/socket.mock.js
@@ -1,4 +1,5 @@
'use strict';
+const angular = require('angular');
angular.module('socketMock', [])
.factory('socket', function() {
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/ui-router(uirouter)/ui-router.mock.js b/templates/app/client/components/ui-router(uirouter)/ui-router.mock.js
index a5a1bf413..35b8d030b 100644
--- a/templates/app/client/components/ui-router(uirouter)/ui-router.mock.js
+++ b/templates/app/client/components/ui-router(uirouter)/ui-router.mock.js
@@ -1,4 +1,5 @@
'use strict';
+const angular = require('angular');
angular.module('stateMock', []);
angular.module('stateMock').service('$state', function($q) {
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..92650a052
--- /dev/null
+++ b/templates/app/client/polyfills.js
@@ -0,0 +1,36 @@
+// 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';
+
+interface IPolyFillErrorConstructor extends ErrorConstructor {
+ stackTraceLimit: any;
+}<% } %>
+
+if(!ENV) {
+ var ENV = 'development';
+}
+
+if(ENV === 'production') {
+ // Production
+} else {
+ // Development
+
+ <%_ if(filters.ts) { _%>
+ (
Error).stackTraceLimit = Infinity;<% } else { %>
+ Error.stackTraceLimit = Infinity;
+ <% } %>
+ // require('zone.js/dist/long-stack-trace-zone');
+}
\ No newline at end of file
diff --git a/templates/app/e2e/account(auth)/login/login.spec(jasmine).js b/templates/app/e2e/account(auth)/login/login.spec(jasmine).js
index 03ea404df..780ed57d0 100644
--- a/templates/app/e2e/account(auth)/login/login.spec(jasmine).js
+++ b/templates/app/e2e/account(auth)/login/login.spec(jasmine).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()).toBe(config.baseUrl + '/');
- expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name);
+ return page.login(testUser).then(() => {
+ var navbar = require('../../components/navbar/navbar.po');
+
+ return browser.wait(
+ () => element(by.css('.hero-unit')),
+ 5000,
+ `Didn't find .hero-unit after 5s`
+ ).then(() => {
+ expect(browser.getCurrentUrl()).toBe(config.baseUrl + '/');
+ expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name);
+ });
+ });
});
it('should indicate login failures', function() {
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..fd11f4131 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);
+ return page.login(testUser).then(() => {
+ var navbar = require('../../components/navbar/navbar.po');
+
+ return browser.wait(
+ () => element(by.css('.hero-unit')),
+ 5000,
+ `Didn't find .hero-unit after 5s`
+ ).then(() => {
+ <%= 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.js
similarity index 52%
rename from templates/app/gulpfile.babel(gulp).js
rename to templates/app/gulpfile.babel.js
index d0ce99227..3272062f0 100644
--- a/templates/app/gulpfile.babel(gulp).js
+++ b/templates/app/gulpfile.babel.js
@@ -6,30 +6,32 @@ 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';
import lazypipe from 'lazypipe';
-import {stream as wiredep} from 'wiredep';
import nodemon from 'nodemon';
import {Server as KarmaServer} from 'karma';
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 clientPath = require('./bower.json').appPath || 'client';
+const clientPath = 'client';
const serverPath = 'server';
const paths = {
client: {
assets: `${clientPath}/assets/**/*`,
images: `${clientPath}/assets/images/**/*`,
+ revManifest: `${clientPath}/assets/rev-manifest.json`,
scripts: [
- `${clientPath}/**/!(*.spec|*.mock).<%= scriptExt %>`,
- `!${clientPath}/bower_components/**/*`<% if(filters.ts) { %>,
+ `${clientPath}/**/!(*.spec|*.mock).<%= scriptExt %>`<% if(filters.ts) { %>,
`!${clientPath}/{typings,test_typings}/**/*`<% } %>
],
styles: [`${clientPath}/{app,components}/**/*.<%= styleExt %>`],
@@ -37,8 +39,7 @@ const paths = {
views: `${clientPath}/{app,components}/**/*.<%= templateExt %>`,
mainView: `${clientPath}/index.html`,
test: [`${clientPath}/{app,components}/**/*.{spec,mock}.<%= scriptExt %>`],
- e2e: ['e2e/**/*.spec.js'],
- bower: `${clientPath}/bower_components/`
+ e2e: ['e2e/**/*.spec.js']
},
server: {
scripts: [
@@ -91,61 +92,47 @@ 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
********************/
let lintClientScripts = lazypipe()<% if(filters.babel) { %>
- .pipe(plugins.jshint, `${clientPath}/.jshintrc`)
- .pipe(plugins.jshint.reporter, 'jshint-stylish');<% } %><% if(filters.ts) { %>
+ .pipe(plugins.eslint, `${clientPath}/.eslintrc`)
+ .pipe(plugins.eslint.format);<% } %><% if(filters.ts) { %>
+ .pipe(plugins.tslint, require(`./${clientPath}/tslint.json`))
+ .pipe(plugins.tslint.report, 'verbose', {emitError: false});<% } %>
+
+const lintClientTestScripts = lazypipe()
+ <%_ if(filters.babel) { -%>
+ .pipe(plugins.eslint, {
+ configFile: `${clientPath}/.eslintrc`,
+ envs: [
+ 'browser',
+ 'es6',
+ 'mocha'
+ ]
+ })
+ .pipe(plugins.eslint.format);
+ <%_ } -%>
+ <%_ 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`)
- .pipe(plugins.jshint.reporter, 'jshint-stylish');
+ .pipe(plugins.eslint, `${serverPath}/.eslintrc`)
+ .pipe(plugins.eslint.format);
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.concat, 'app.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, '.');<% } %>
+ .pipe(plugins.eslint, {
+ configFile: `${serverPath}/.eslintrc`,
+ envs: [
+ 'node',
+ 'es6',
+ 'mocha'
+ ]
+ })
+ .pipe(plugins.eslint.format);
let transpileServer = lazypipe()
.pipe(plugins.sourcemaps.init)
@@ -212,89 +199,59 @@ gulp.task('env:prod', () => {
********************/
gulp.task('inject', cb => {
- runSequence(['inject:js', 'inject:css'<% if(!filters.css) { %>, 'inject:<%= styleExt %>'<% } %><% if(filters.ts) { %>, 'inject:tsconfig'<% } %>], cb);
+ runSequence(['inject:<%= styleExt %>'], 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));
-});<% if(filters.ts) { %>
-
-function injectTsConfig(filesGlob, tsconfigPath){
- let src = gulp.src(filesGlob, {read: false})
- .pipe(plugins.sort());
-
- return gulp.src(tsconfigPath)
- .pipe(plugins.inject(src, {
- starttag: '"files": [',
- endtag: ']',
- transform: (filepath, file, i, length) => {
- return `"${filepath.substr(1)}"${i + 1 < length ? ',' : ''}`;
- }
- }))
- .pipe(gulp.dest('./'));
-}
-
-gulp.task('inject:tsconfig', () => {
- return injectTsConfig([
- `${clientPath}/**/!(*.spec|*.mock).ts`,
- `!${clientPath}/bower_components/**/*`,
- `typings/main.d.ts`
- ],
- './tsconfig.client.json');
-});
-
-gulp.task('inject:tsconfigTest', () => {
- return injectTsConfig([
- `${clientPath}/**/+(*.spec|*.mock).ts`,
- `!${clientPath}/bower_components/**/*`,
- `typings/main.d.ts`
- ],
- './tsconfig.client.test.json');
-});<% } %>
-
-gulp.task('inject:css', () => {
- return gulp.src(paths.client.mainView)
- .pipe(plugins.inject(
- gulp.src(`${clientPath}/{app,components}/**/*.css`, {read: false})
- .pipe(plugins.sort()),
- {
- starttag: '',
- endtag: '',
- transform: (filepath) => ''
- }))
- .pipe(gulp.dest(clientPath));
-});<% if(!filters.css) { %>
-
gulp.task('inject:<%= styleExt %>', () => {
return gulp.src(paths.client.mainStyle)
.pipe(plugins.inject(
gulp.src(_.union(paths.client.styles, ['!' + paths.client.mainStyle]), {read: false})
.pipe(plugins.sort()),
{
- <%_ if(filters.stylus) { _%>
- starttag: '/* inject:styl */',
+ <%_ if(filters.stylus || filters.css) { -%>
+ starttag: '/* inject:<%= styleExt %> */',
endtag: '/* endinject */',
- <%_ } _%>
+ <%_ } -%>
transform: (filepath) => {
let newPath = filepath
.replace(`/${clientPath}/app/`, '')
.replace(`/${clientPath}/components/`, '../components/')
- .replace(/_(.*).<%= styleExt %>/, (match, p1, offset, string) => p1)
- .replace('.<%= styleExt %>', '');
+ .replace(/_(.*).<%= styleExt %>/, (match, p1, offset, string) => p1)<% if(filters.css) { %>;<% } else { %>
+ .replace('.<%= styleExt %>', '');<% } %>
return `@import '${newPath}';`;
}
}))
.pipe(gulp.dest(`${clientPath}/app`));
-});<% } %><% if(filters.ts) { %>
+});
+
+gulp.task('webpack:dev', function() {
+ const webpackDevConfig = makeWebpackConfig({ DEV: true });
+ return gulp.src(webpackDevConfig.entry.app)
+ .pipe(plugins.plumber())
+ .pipe(webpack(webpackDevConfig))
+ .pipe(gulp.dest('.tmp'));
+});
+
+gulp.task('webpack:dist', function() {
+ const webpackDistConfig = makeWebpackConfig({ BUILD: true });
+ return gulp.src(webpackDistConfig.entry.app)
+ .pipe(webpack(webpackDistConfig))
+ .pipe(gulp.dest(`${paths.dist}/client`));
+});
+
+gulp.task('webpack:test', function() {
+ const webpackTestConfig = makeWebpackConfig({ TEST: true });
+ return gulp.src(webpackTestConfig.entry.app)
+ .pipe(webpack(webpackTestConfig))
+ .pipe(gulp.dest('.tmp'));
+});
+
+gulp.task('webpack:e2e', function() {
+ const webpackE2eConfig = makeWebpackConfig({ E2E: true });
+ return gulp.src(webpackE2eConfig.entry.app)
+ .pipe(webpack(webpackE2eConfig))
+ .pipe(gulp.dest('.tmp'));
+});<% if(filters.ts) { %>
// Install DefinitelyTyped TypeScript definition files
gulp.task('typings', () => {
@@ -310,35 +267,8 @@ gulp.task('styles', () => {
<%_ } _%>
.pipe(styles())
.pipe(gulp.dest('.tmp/app'));
-});<% if(filters.ts) { %>
-
-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', () => {
return gulp.src(_.union(paths.server.scripts, paths.server.json))
.pipe(transpileServer())
@@ -350,8 +280,7 @@ gulp.task('lint:scripts', cb => runSequence(['lint:scripts:client', 'lint:script
gulp.task('lint:scripts:client', () => {
return gulp.src(_.union(
paths.client.scripts,
- _.map(paths.client.test, blob => '!' + blob),
- [`!${clientPath}/app/app.constant.<%= scriptExt %>`]
+ _.map(paths.client.test, blob => '!' + blob)
))
.pipe(lintClientScripts());
});
@@ -381,7 +310,7 @@ gulp.task('clean:tmp', () => del(['.tmp/**/*'], {dot: true}));
gulp.task('start:client', cb => {
whenServerReady(() => {
- open('http://localhost:' + config.port);
+ open('http://localhost:' + config.browserSyncPort);
cb();
});
});
@@ -417,46 +346,48 @@ gulp.task('start:server:debug', () => {
gulp.task('watch', () => {
var testFiles = _.union(paths.client.test, paths.server.test.unit, paths.server.test.integration);
- 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());
+ .pipe(lintServerScripts());
- 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',
+ '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',
+ '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 => {
@@ -468,17 +399,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);
});
@@ -489,7 +409,6 @@ gulp.task('test:server', cb => {
'env:test',
'mocha:unit',
'mocha:integration',
- 'mocha:coverage',
cb);
});
@@ -503,139 +422,91 @@ gulp.task('mocha:integration', () => {
.pipe(mocha());
});
-gulp.task('test:client', ['wiredep:test', 'constant'<% if(filters.ts) { %>, 'transpile:client', 'transpile:client:test'<% } %>], (done) => {
+gulp.task('test:server: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', 'env:all', 'env:test', 'start:server', 'webdriver_update'], cb => {
+ gulp.src(paths.client.e2e)
+ .pipe(protractor({
+ configFile: 'protractor.conf.js',
+ }))
+ .on('error', e => { throw e })
+ .on('end', () => { process.exit() });
+});
+
+gulp.task('test:client', 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('./'));
+ }, err => {
+ done(err);
+ process.exit(err);
+ }).start();
});
/********************
* 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:server',
[
- 'transpile:client',
- 'transpile:server'
+ 'build:images',
+ 'typings'
],
[
- 'build:images',
'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({
- name: '<%= scriptAppName %>.constants',
- deps: [],
- wrap: true,
- stream: true,
- constants: { appConfig: sharedConfig }
- })
- .pipe(plugins.rename({
- basename: 'app.constant'
- }))
- .pipe(gulp.dest(`${clientPath}/app/`))
-});
-
gulp.task('build:images', () => {
return gulp.src(paths.client.images)
.pipe(plugins.imagemin({
@@ -645,13 +516,19 @@ gulp.task('build:images', () => {
}))
.pipe(plugins.rev())
.pipe(gulp.dest(`${paths.dist}/${clientPath}/assets/images`))
- .pipe(plugins.rev.manifest(`${paths.dist}/${clientPath}/assets/rev-manifest.json`, {
+ .pipe(plugins.rev.manifest(`${paths.dist}/${paths.client.revManifest}`, {
base: `${paths.dist}/${clientPath}/assets`,
merge: true
}))
.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.dist}/${paths.client.revManifest}`)}))
+ .pipe(gulp.dest('dist/client'));
+});
+
gulp.task('copy:extras', () => {
return gulp.src([
`${clientPath}/favicon.ico`,
@@ -661,10 +538,33 @@ 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', () => {
+ return gulp.src('node_modules/{bootstrap,font-awesome}/fonts/*')
+ .pipe(flatten())
+ .pipe(gulp.dest(`${clientPath}/assets/fonts`));
+});
+gulp.task('copy:fonts:dist', () => {
+ return gulp.src('node_modules/{bootstrap,font-awesome}/fonts/*')
+ .pipe(flatten())
+ .pipe(gulp.dest(`${paths.dist}/${clientPath}/assets/fonts`));
});
gulp.task('copy:assets', () => {
@@ -674,61 +574,11 @@ gulp.task('copy:assets', () => {
gulp.task('copy:server', () => {
return gulp.src([
- 'package.json',
- 'bower.json',
- '.bowerrc'
+ 'package.json'
], {cwdbase: true})
.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 9b19748c8..7297c1bd0 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,48 @@ 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'),
+ require('karma-sourcemap-loader')
+ ],
// list of files / patterns to exclude
exclude: [],
@@ -74,7 +80,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/mocha.global(gulp).js b/templates/app/mocha.global.js
similarity index 100%
rename from templates/app/mocha.global(gulp).js
rename to templates/app/mocha.global.js
diff --git a/templates/app/server/.eslintrc b/templates/app/server/.eslintrc
new file mode 100644
index 000000000..849296d61
--- /dev/null
+++ b/templates/app/server/.eslintrc
@@ -0,0 +1,6 @@
+{
+ "extends": "../.eslintrc",
+ "env": {
+ "node": true
+ }
+}
diff --git a/templates/app/server/.jshintrc b/templates/app/server/.jshintrc
deleted file mode 100644
index 69f3b00e3..000000000
--- a/templates/app/server/.jshintrc
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "expr": true,
- "node": true,
- "esnext": true,
- "bitwise": true,
- "eqeqeq": true,
- "immed": true,
- "latedef": "nofunc",
- "newcap": true,
- "noarg": true,
- "undef": true,
- "smarttabs": true,
- "asi": true,
- "debug": true
-}
diff --git a/templates/app/server/.jshintrc-spec b/templates/app/server/.jshintrc-spec
deleted file mode 100644
index 8c9871ce0..000000000
--- a/templates/app/server/.jshintrc-spec
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "extends": ".jshintrc",
- "globals": {<% if (filters.jasmine) { %>
- "jasmine": true,<% } %>
- "describe": true,
- "it": true,
- "before": true,
- "beforeEach": true,
- "after": true,
- "afterEach": true,
- "expect": true,
- "assert": true,
- "sinon": true
- }
-}
diff --git a/templates/app/server/api/user(auth)/user.controller.js b/templates/app/server/api/user(auth)/user.controller.js
index 66229fc22..c3e0d50ed 100644
--- a/templates/app/server/api/user(auth)/user.controller.js
+++ b/templates/app/server/api/user(auth)/user.controller.js
@@ -89,7 +89,7 @@ export function show(req, res, next) {
*/
export function destroy(req, res) {
<% if (filters.mongooseModels) { %>return User.findByIdAndRemove(req.params.id).exec()<% }
- if (filters.sequelizeModels) { %>return User.destroy({ _id: req.params.id })<% } %>
+ if (filters.sequelizeModels) { %>return User.destroy({ where: { _id: req.params.id } })<% } %>
.then(function() {
res.status(204).end();
})
diff --git a/templates/app/server/api/user(auth)/user.model(mongooseModels).js b/templates/app/server/api/user(auth)/user.model(mongooseModels).js
index 5394bad3c..c67632162 100644
--- a/templates/app/server/api/user(auth)/user.model(mongooseModels).js
+++ b/templates/app/server/api/user(auth)/user.model(mongooseModels).js
@@ -1,9 +1,8 @@
'use strict';
-
+/*eslint no-invalid-this:0*/
import crypto from 'crypto';
-import mongoose from 'mongoose';
mongoose.Promise = require('bluebird');
-import {Schema} from 'mongoose';<% if (filters.oauth) { %>
+import mongoose, {Schema} from 'mongoose';<% if(filters.oauth) { %>
const authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %>
@@ -12,13 +11,17 @@ var UserSchema = new Schema({
email: {
type: String,
lowercase: true,
- required: <% if(filters.oauth) { %>function() {
- if (authTypes.indexOf(this.provider) === -1) {
+ <%_ if(filters.oauth) { -%>
+ required() {
+ if(authTypes.indexOf(this.provider) === -1) {
return true;
} else {
return false;
}
- }<% } else { %>true<% } %>
+ }
+ <%_ } else { -%>
+ required: true
+ <%_ } -%>
},
role: {
type: String,
@@ -27,7 +30,7 @@ var UserSchema = new Schema({
password: {
type: String,
required: <% if(filters.oauth) { %>function() {
- if (authTypes.indexOf(this.provider) === -1) {
+ if(authTypes.indexOf(this.provider) === -1) {
return true;
} else {
return false;
@@ -35,9 +38,9 @@ var UserSchema = new Schema({
}<% } else { %>true<% } %>
},
provider: String,
- salt: String<% if (filters.oauth) { %>,<% if (filters.facebookAuth) { %>
- facebook: {},<% } %><% if (filters.twitterAuth) { %>
- twitter: {},<% } %><% if (filters.googleAuth) { %>
+ salt: String<% if(filters.oauth) { %>,<% if(filters.facebookAuth) { %>
+ facebook: {},<% } %><% if(filters.twitterAuth) { %>
+ twitter: {},<% } %><% if(filters.googleAuth) { %>
google: {},<% } %>
github: {}<% } %>
});
@@ -51,8 +54,8 @@ UserSchema
.virtual('profile')
.get(function() {
return {
- 'name': this.name,
- 'role': this.role
+ name: this.name,
+ role: this.role
};
});
@@ -61,8 +64,8 @@ UserSchema
.virtual('token')
.get(function() {
return {
- '_id': this._id,
- 'role': this.role
+ _id: this._id,
+ role: this.role
};
});
@@ -73,18 +76,20 @@ UserSchema
// Validate empty email
UserSchema
.path('email')
- .validate(function(email) {<% if (filters.oauth) { %>
- if (authTypes.indexOf(this.provider) !== -1) {
+ .validate(function(email) {
+ <%_ if(filters.oauth) { -%>
+ if(authTypes.indexOf(this.provider) !== -1) {
return true;
- }<% } %>
+ }
+ <%_ } -%>
return email.length;
}, 'Email cannot be blank');
// Validate empty password
UserSchema
.path('password')
- .validate(function(password) {<% if (filters.oauth) { %>
- if (authTypes.indexOf(this.provider) !== -1) {
+ .validate(function(password) {<% if(filters.oauth) { %>
+ if(authTypes.indexOf(this.provider) !== -1) {
return true;
}<% } %>
return password.length;
@@ -94,15 +99,16 @@ UserSchema
UserSchema
.path('email')
.validate(function(value, respond) {
- var self = this;
- <%_ if(filters.oauth) { _%>
- if (authTypes.indexOf(this.provider) !== -1) {
+ <%_ if(filters.oauth) { -%>
+ if(authTypes.indexOf(this.provider) !== -1) {
return respond(true);
- }<% } %>
+ }
+
+ <%_ } -%>
return this.constructor.findOne({ email: value }).exec()
- .then(function(user) {
- if (user) {
- if (self.id === user.id) {
+ .then(user => {
+ if(user) {
+ if(this.id === user.id) {
return respond(true);
}
return respond(false);
@@ -124,13 +130,13 @@ var validatePresenceOf = function(value) {
UserSchema
.pre('save', function(next) {
// Handle new/update passwords
- if (!this.isModified('password')) {
+ if(!this.isModified('password')) {
return next();
}
- if (!validatePresenceOf(this.password)) {
- <% if (filters.oauth) { %>if (authTypes.indexOf(this.provider) === -1) {
- <% } %>return next(new Error('Invalid password'));<% if (filters.oauth) { %>
+ if(!validatePresenceOf(this.password)) {
+ <% if(filters.oauth) { %>if(authTypes.indexOf(this.provider) === -1) {
+ <% } %>return next(new Error('Invalid password'));<% if(filters.oauth) { %>
} else {
return next();
}<% } %>
@@ -138,16 +144,16 @@ UserSchema
// Make salt with a callback
this.makeSalt((saltErr, salt) => {
- if (saltErr) {
+ if(saltErr) {
return next(saltErr);
}
this.salt = salt;
this.encryptPassword(this.password, (encryptErr, hashedPassword) => {
- if (encryptErr) {
+ if(encryptErr) {
return next(encryptErr);
}
this.password = hashedPassword;
- next();
+ return next();
});
});
});
@@ -165,19 +171,19 @@ UserSchema.methods = {
* @api public
*/
authenticate(password, callback) {
- if (!callback) {
+ if(!callback) {
return this.password === this.encryptPassword(password);
}
this.encryptPassword(password, (err, pwdGen) => {
- if (err) {
+ if(err) {
return callback(err);
}
- if (this.password === pwdGen) {
- callback(null, true);
+ if(this.password === pwdGen) {
+ return callback(null, true);
} else {
- callback(null, false);
+ return callback(null, false);
}
});
},
@@ -185,7 +191,7 @@ UserSchema.methods = {
/**
* Make salt
*
- * @param {Number} byteSize Optional salt byte size, default to 16
+ * @param {Number} [byteSize] - Optional salt byte size, default to 16
* @param {Function} callback
* @return {String}
* @api public
@@ -193,26 +199,24 @@ UserSchema.methods = {
makeSalt(byteSize, callback) {
var defaultByteSize = 16;
- if (typeof arguments[0] === 'function') {
+ if(typeof arguments[0] === 'function') {
callback = arguments[0];
byteSize = defaultByteSize;
- } else if (typeof arguments[1] === 'function') {
+ } else if(typeof arguments[1] === 'function') {
callback = arguments[1];
+ } else {
+ throw new Error('Missing Callback');
}
- if (!byteSize) {
+ if(!byteSize) {
byteSize = defaultByteSize;
}
- if (!callback) {
- return crypto.randomBytes(byteSize).toString('base64');
- }
-
return crypto.randomBytes(byteSize, (err, salt) => {
- if (err) {
- callback(err);
+ if(err) {
+ return callback(err);
} else {
- callback(null, salt.toString('base64'));
+ return callback(null, salt.toString('base64'));
}
});
},
@@ -226,8 +230,8 @@ UserSchema.methods = {
* @api public
*/
encryptPassword(password, callback) {
- if (!password || !this.salt) {
- if (!callback) {
+ if(!password || !this.salt) {
+ if(!callback) {
return null;
} else {
return callback('Missing password or salt');
@@ -238,13 +242,13 @@ UserSchema.methods = {
var defaultKeyLength = 64;
var salt = new Buffer(this.salt, 'base64');
- if (!callback) {
+ if(!callback) {
return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength)
.toString('base64');
}
return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, (err, key) => {
- if (err) {
+ if(err) {
callback(err);
} else {
callback(null, key.toString('base64'));
diff --git a/templates/app/server/api/user(auth)/user.model(sequelizeModels).js b/templates/app/server/api/user(auth)/user.model(sequelizeModels).js
index e2c625378..58e8f5ae2 100644
--- a/templates/app/server/api/user(auth)/user.model(sequelizeModels).js
+++ b/templates/app/server/api/user(auth)/user.model(sequelizeModels).js
@@ -1,13 +1,13 @@
'use strict';
-import crypto from 'crypto';<% if (filters.oauth) { %>
+import crypto from 'crypto';<% if(filters.oauth) { %>
var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %>
var validatePresenceOf = function(value) {
return value && value.length;
};
-module.exports = function(sequelize, DataTypes) {
+export default function(sequelize, DataTypes) {
var User = sequelize.define('User', {
_id: {
@@ -37,9 +37,9 @@ module.exports = function(sequelize, DataTypes) {
}
},
provider: DataTypes.STRING,
- salt: DataTypes.STRING<% if (filters.oauth) { %>,<% if (filters.facebookAuth) { %>
- facebook: DataTypes.JSON,<% } %><% if (filters.twitterAuth) { %>
- twitter: DataTypes.JSON,<% } %><% if (filters.googleAuth) { %>
+ salt: DataTypes.STRING<% if(filters.oauth) { %>,<% if(filters.facebookAuth) { %>
+ facebook: DataTypes.JSON,<% } %><% if(filters.twitterAuth) { %>
+ twitter: DataTypes.JSON,<% } %><% if(filters.googleAuth) { %>
google: DataTypes.JSON,<% } %>
github: DataTypes.JSON<% } %>
@@ -52,16 +52,16 @@ module.exports = function(sequelize, DataTypes) {
// Public profile information
profile: function() {
return {
- 'name': this.name,
- 'role': this.role
+ name: this.name,
+ role: this.role
};
},
// Non-sensitive info we'll be putting in the token
token: function() {
return {
- '_id': this._id,
- 'role': this.role
+ _id: this._id,
+ role: this.role
};
}
},
@@ -70,25 +70,25 @@ module.exports = function(sequelize, DataTypes) {
* Pre-save hooks
*/
hooks: {
- beforeBulkCreate: function(users, fields, fn) {
+ beforeBulkCreate(users, fields, fn) {
var totalUpdated = 0;
- users.forEach(function(user) {
- user.updatePassword(function(err) {
- if (err) {
+ users.forEach(user => {
+ user.updatePassword(err => {
+ if(err) {
return fn(err);
}
totalUpdated += 1;
- if (totalUpdated === users.length) {
+ if(totalUpdated === users.length) {
return fn();
}
});
});
},
- beforeCreate: function(user, fields, fn) {
+ beforeCreate(user, fields, fn) {
user.updatePassword(fn);
},
- beforeUpdate: function(user, fields, fn) {
- if (user.changed('password')) {
+ beforeUpdate(user, fields, fn) {
+ if(user.changed('password')) {
return user.updatePassword(fn);
}
fn();
@@ -107,18 +107,18 @@ module.exports = function(sequelize, DataTypes) {
* @return {Boolean}
* @api public
*/
- authenticate: function(password, callback) {
- if (!callback) {
+ authenticate(password, callback) {
+ if(!callback) {
return this.password === this.encryptPassword(password);
}
var _this = this;
this.encryptPassword(password, function(err, pwdGen) {
- if (err) {
+ if(err) {
callback(err);
}
- if (_this.password === pwdGen) {
+ if(_this.password === pwdGen) {
callback(null, true);
}
else {
@@ -130,32 +130,29 @@ module.exports = function(sequelize, DataTypes) {
/**
* Make salt
*
- * @param {Number} byteSize Optional salt byte size, default to 16
+ * @param {Number} [byteSize] - Optional salt byte size, default to 16
* @param {Function} callback
* @return {String}
* @api public
*/
- makeSalt: function(byteSize, callback) {
+ makeSalt(byteSize, callback) {
var defaultByteSize = 16;
- if (typeof arguments[0] === 'function') {
+ if(typeof arguments[0] === 'function') {
callback = arguments[0];
byteSize = defaultByteSize;
- }
- else if (typeof arguments[1] === 'function') {
+ } else if(typeof arguments[1] === 'function') {
callback = arguments[1];
+ } else {
+ throw new Error('Missing Callback');
}
- if (!byteSize) {
+ if(!byteSize) {
byteSize = defaultByteSize;
}
- if (!callback) {
- return crypto.randomBytes(byteSize).toString('base64');
- }
-
return crypto.randomBytes(byteSize, function(err, salt) {
- if (err) {
+ if(err) {
callback(err);
}
return callback(null, salt.toString('base64'));
@@ -170,26 +167,23 @@ module.exports = function(sequelize, DataTypes) {
* @return {String}
* @api public
*/
- encryptPassword: function(password, callback) {
- if (!password || !this.salt) {
- if (!callback) {
- return null;
- }
- return callback(null);
+ encryptPassword(password, callback) {
+ if(!password || !this.salt) {
+ return callback ? callback(null) : null;
}
var defaultIterations = 10000;
var defaultKeyLength = 64;
var salt = new Buffer(this.salt, 'base64');
- if (!callback) {
+ if(!callback) {
return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength)
.toString('base64');
}
return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength,
function(err, key) {
- if (err) {
+ if(err) {
callback(err);
}
return callback(null, key.toString('base64'));
@@ -203,31 +197,28 @@ module.exports = function(sequelize, DataTypes) {
* @return {String}
* @api public
*/
- updatePassword: function(fn) {
+ updatePassword(fn) {
// Handle new/update passwords
- if (this.password) {
- if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) {
- fn(new Error('Invalid password'));
- }
+ if(!this.password) return fn(null);
- // Make salt with a callback
- var _this = this;
- this.makeSalt(function(saltErr, salt) {
- if (saltErr) {
- fn(saltErr);
+ if(!validatePresenceOf(this.password)<% if(filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) {
+ fn(new Error('Invalid password'));
+ }
+
+ // Make salt with a callback
+ this.makeSalt((saltErr, salt) => {
+ if(saltErr) {
+ return fn(saltErr);
+ }
+ this.salt = salt;
+ this.encryptPassword(this.password, (encryptErr, hashedPassword) => {
+ if(encryptErr) {
+ fn(encryptErr);
}
- _this.salt = salt;
- _this.encryptPassword(_this.password, function(encryptErr, hashedPassword) {
- if (encryptErr) {
- fn(encryptErr);
- }
- _this.password = hashedPassword;
- fn(null);
- });
+ this.password = hashedPassword;
+ fn(null);
});
- } else {
- fn(null);
- }
+ });
}
}
});
diff --git a/templates/app/server/auth(auth)/facebook(facebookAuth)/passport.js b/templates/app/server/auth(auth)/facebook(facebookAuth)/passport.js
index e8c9f9d9d..2573c2454 100644
--- a/templates/app/server/auth(auth)/facebook(facebookAuth)/passport.js
+++ b/templates/app/server/auth(auth)/facebook(facebookAuth)/passport.js
@@ -12,15 +12,15 @@ export function setup(User, config) {
]
},
function(accessToken, refreshToken, profile, done) {
- <% if (filters.mongooseModels) { %>User.findOne({'facebook.id': profile.id}).exec()<% }
- if (filters.sequelizeModels) { %>User.find({where:{'facebook.id': profile.id}})<% } %>
+ <% if(filters.mongooseModels) { %>User.findOne({'facebook.id': profile.id}).exec()<% }
+ if(filters.sequelizeModels) { %>User.find({where:{'facebook.id': profile.id}})<% } %>
.then(user => {
- if (user) {
+ if(user) {
return done(null, user);
}
- <% if (filters.mongooseModels) { %>user = new User({<% }
- if (filters.sequelizeModels) { %>user = User.build({<% } %>
+ <% if(filters.mongooseModels) { %>user = new User({<% }
+ if(filters.sequelizeModels) { %>user = User.build({<% } %>
name: profile.displayName,
email: profile.emails[0].value,
role: 'user',
@@ -28,7 +28,7 @@ export function setup(User, config) {
facebook: profile._json
});
user.save()
- .then(user => done(null, user))
+ .then(savedUser => done(null, savedUser))
.catch(err => done(err));
})
.catch(err => done(err));
diff --git a/templates/app/server/auth(auth)/google(googleAuth)/passport.js b/templates/app/server/auth(auth)/google(googleAuth)/passport.js
index 9d6d2193d..4e8f71e66 100644
--- a/templates/app/server/auth(auth)/google(googleAuth)/passport.js
+++ b/templates/app/server/auth(auth)/google(googleAuth)/passport.js
@@ -8,15 +8,15 @@ export function setup(User, config) {
callbackURL: config.google.callbackURL
},
function(accessToken, refreshToken, profile, done) {
- <% if (filters.mongooseModels) { %>User.findOne({'google.id': profile.id}).exec()<% }
- if (filters.sequelizeModels) { %>User.find({where:{'google.id': profile.id}})<% } %>
+ <% if(filters.mongooseModels) { %>User.findOne({'google.id': profile.id}).exec()<% }
+ if(filters.sequelizeModels) { %>User.find({where:{'google.id': profile.id}})<% } %>
.then(user => {
- if (user) {
+ if(user) {
return done(null, user);
}
- <% if (filters.mongooseModels) { %>user = new User({<% }
- if (filters.sequelizeModels) { %>user = User.build({<% } %>
+ <% if(filters.mongooseModels) { %>user = new User({<% }
+ if(filters.sequelizeModels) { %>user = User.build({<% } %>
name: profile.displayName,
email: profile.emails[0].value,
role: 'user',
@@ -24,9 +24,9 @@ export function setup(User, config) {
provider: 'google',
google: profile._json
});
- <% if (filters.mongooseModels) { %>user.save()<% }
- if (filters.sequelizeModels) { %>user.save()<% } %>
- .then(user => done(null, user))
+ <% if(filters.mongooseModels) { %>user.save()<% }
+ if(filters.sequelizeModels) { %>user.save()<% } %>
+ .then(savedUser => done(null, savedUser))
.catch(err => done(err));
})
.catch(err => done(err));
diff --git a/templates/app/server/auth(auth)/local/index.js b/templates/app/server/auth(auth)/local/index.js
index 8002a8442..08ebf69b2 100644
--- a/templates/app/server/auth(auth)/local/index.js
+++ b/templates/app/server/auth(auth)/local/index.js
@@ -9,16 +9,16 @@ var router = express.Router();
router.post('/', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
var error = err || info;
- if (error) {
+ if(error) {
return res.status(401).json(error);
}
- if (!user) {
+ if(!user) {
return res.status(404).json({message: 'Something went wrong, please try again.'});
}
var token = signToken(user._id, user.role);
res.json({ token });
- })(req, res, next)
+ })(req, res, next);
});
export default router;
diff --git a/templates/app/server/auth(auth)/local/passport.js b/templates/app/server/auth(auth)/local/passport.js
index 430bbec38..125f482bc 100644
--- a/templates/app/server/auth(auth)/local/passport.js
+++ b/templates/app/server/auth(auth)/local/passport.js
@@ -2,25 +2,25 @@ import passport from 'passport';
import {Strategy as LocalStrategy} from 'passport-local';
function localAuthenticate(User, email, password, done) {
- <% if (filters.mongooseModels) { %>User.findOne({
+ <% if(filters.mongooseModels) { %>User.findOne({
email: email.toLowerCase()
}).exec()<% }
- if (filters.sequelizeModels) { %>User.find({
+ if(filters.sequelizeModels) { %>User.find({
where: {
email: email.toLowerCase()
}
})<% } %>
.then(user => {
- if (!user) {
+ if(!user) {
return done(null, false, {
message: 'This email is not registered.'
});
}
user.authenticate(password, function(authError, authenticated) {
- if (authError) {
+ if(authError) {
return done(authError);
}
- if (!authenticated) {
+ if(!authenticated) {
return done(null, false, { message: 'This password is not correct.' });
} else {
return done(null, user);
@@ -30,11 +30,11 @@ function localAuthenticate(User, email, password, done) {
.catch(err => done(err));
}
-export function setup(User, config) {
+export function setup(User/*, config*/) {
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password' // this is the virtual field on the model
- }, function(email, password, done) {<% if (filters.models) { %>
+ }, function(email, password, done) {<% if(filters.models) { %>
return localAuthenticate(User, email, password, done);
<% } %> }));
}
diff --git a/templates/app/server/auth(auth)/twitter(twitterAuth)/passport.js b/templates/app/server/auth(auth)/twitter(twitterAuth)/passport.js
index 16c34339c..f13b4cfe7 100644
--- a/templates/app/server/auth(auth)/twitter(twitterAuth)/passport.js
+++ b/templates/app/server/auth(auth)/twitter(twitterAuth)/passport.js
@@ -8,24 +8,24 @@ export function setup(User, config) {
callbackURL: config.twitter.callbackURL
},
function(token, tokenSecret, profile, done) {
- <% if (filters.mongooseModels) { %>User.findOne({'twitter.id': profile.id}).exec()<% }
- if (filters.sequelizeModels) { %>User.find({where:{'twitter.id': profile.id}})<% } %>
+ <% if(filters.mongooseModels) { %>User.findOne({'twitter.id': profile.id}).exec()<% }
+ if(filters.sequelizeModels) { %>User.find({where:{'twitter.id': profile.id}})<% } %>
.then(user => {
- if (user) {
+ if(user) {
return done(null, user);
}
- <% if (filters.mongooseModels) { %>user = new User({<% }
- if (filters.sequelizeModels) { %>user = User.build({<% } %>
+ <% if(filters.mongooseModels) { %>user = new User({<% }
+ if(filters.sequelizeModels) { %>user = User.build({<% } %>
name: profile.displayName,
username: profile.username,
role: 'user',
provider: 'twitter',
twitter: profile._json
});
- <% if (filters.mongooseModels) { %>user.save()<% }
- if (filters.sequelizeModels) { %>user.save()<% } %>
- .then(user => done(null, user))
+ <% if(filters.mongooseModels) { %>user.save()<% }
+ if(filters.sequelizeModels) { %>user.save()<% } %>
+ .then(savedUser => done(null, savedUser))
.catch(err => done(err));
})
.catch(err => done(err));
diff --git a/templates/app/server/components/errors/index.js b/templates/app/server/components/errors/index.js
index 598698175..f1d7e3d44 100644
--- a/templates/app/server/components/errors/index.js
+++ b/templates/app/server/components/errors/index.js
@@ -13,7 +13,7 @@ module.exports[404] = function pageNotFound(req, res) {
res.status(result.status);
res.render(viewFilePath, {}, function(err, html) {
- if (err) {
+ if(err) {
return res.status(result.status).json(result);
}
diff --git a/templates/app/server/config/_local.env.js b/templates/app/server/config/_local.env.js
index 0d915248b..02a5a084b 100644
--- a/templates/app/server/config/_local.env.js
+++ b/templates/app/server/config/_local.env.js
@@ -1,6 +1,6 @@
'use strict';
-// Use local.env.js for environment variables that grunt will set when the server starts locally.
+// Use local.env.js for environment variables that will be set when the server starts locally.
// Use for your api keys, secrets, etc. This file should not be tracked by git.
//
// You will need to set these on the server you deploy to.
diff --git a/templates/app/server/config/_local.env.sample.js b/templates/app/server/config/_local.env.sample.js
index f13d11c3a..79dce5854 100644
--- a/templates/app/server/config/_local.env.sample.js
+++ b/templates/app/server/config/_local.env.sample.js
@@ -1,6 +1,6 @@
'use strict';
-// Use local.env.js for environment variables that grunt will set when the server starts locally.
+// Use local.env.js for environment variables that will be set when the server starts locally.
// Use for your api keys, secrets, etc. This file should not be tracked by git.
//
// You will need to set these on the server you deploy to.
diff --git a/templates/app/server/config/environment/development.js b/templates/app/server/config/environment/development.js
index 472ad5050..fde4920c9 100644
--- a/templates/app/server/config/environment/development.js
+++ b/templates/app/server/config/environment/development.js
@@ -1,4 +1,5 @@
'use strict';
+/*eslint no-process-env:0*/
// Development specific configuration
// ==================================
diff --git a/templates/app/server/config/environment/index.js b/templates/app/server/config/environment/index.js
index 9b0efa232..b82e4ae4f 100644
--- a/templates/app/server/config/environment/index.js
+++ b/templates/app/server/config/environment/index.js
@@ -1,14 +1,15 @@
'use strict';
+/*eslint no-process-env:0*/
-var path = require('path');
-var _ = require('lodash');
+import path from 'path';
+import _ from 'lodash';
-function requiredProcessEnv(name) {
- if (!process.env[name]) {
+/*function requiredProcessEnv(name) {
+ if(!process.env[name]) {
throw new Error('You must set the ' + name + ' environment variable');
}
return process.env[name];
-}
+}*/
// All configurations will extend these options
// ============================================
@@ -16,7 +17,10 @@ var all = {
env: process.env.NODE_ENV,
// Root path of server
- root: path.normalize(__dirname + '/../../..'),
+ root: path.normalize(`${__dirname}/../../..`),
+
+ // Browser-sync port
+ browserSyncPort: process.env.BROWSER_SYNC_PORT || 3000,
// Server port
port: process.env.PORT || <%= devPort %>,
@@ -39,24 +43,24 @@ var all = {
safe: true
}
}
- }<% if (filters.facebookAuth) { %>,
+ }<% if(filters.facebookAuth) { %>,
facebook: {
- clientID: process.env.FACEBOOK_ID || 'id',
+ clientID: process.env.FACEBOOK_ID || 'id',
clientSecret: process.env.FACEBOOK_SECRET || 'secret',
- callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback'
- }<% } %><% if (filters.twitterAuth) { %>,
+ callbackURL: `${process.env.DOMAIN || ''}/auth/facebook/callback`
+ }<% } %><% if(filters.twitterAuth) { %>,
twitter: {
- clientID: process.env.TWITTER_ID || 'id',
+ clientID: process.env.TWITTER_ID || 'id',
clientSecret: process.env.TWITTER_SECRET || 'secret',
- callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback'
- }<% } %><% if (filters.googleAuth) { %>,
+ callbackURL: `${process.env.DOMAIN || ''}/auth/twitter/callback`
+ }<% } %><% if(filters.googleAuth) { %>,
google: {
- clientID: process.env.GOOGLE_ID || 'id',
+ clientID: process.env.GOOGLE_ID || 'id',
clientSecret: process.env.GOOGLE_SECRET || 'secret',
- callbackURL: (process.env.DOMAIN || '') + '/auth/google/callback'
+ callbackURL: `${process.env.DOMAIN || ''}/auth/google/callback`
}<% } %>
};
@@ -65,4 +69,4 @@ var all = {
module.exports = _.merge(
all,
require('./shared'),
- require('./' + process.env.NODE_ENV + '.js') || {});
+ require(`./${process.env.NODE_ENV}.js`) || {});
diff --git a/templates/app/server/config/environment/production.js b/templates/app/server/config/environment/production.js
index 2bd6fe761..188362d88 100644
--- a/templates/app/server/config/environment/production.js
+++ b/templates/app/server/config/environment/production.js
@@ -1,30 +1,30 @@
'use strict';
+/*eslint no-process-env:0*/
// Production specific configuration
// =================================
module.exports = {
// Server IP
- ip: process.env.OPENSHIFT_NODEJS_IP ||
- process.env.IP ||
- undefined,
+ ip: process.env.OPENSHIFT_NODEJS_IP
+ || process.env.ip
+ || undefined,
// Server port
- port: process.env.OPENSHIFT_NODEJS_PORT ||
- process.env.PORT ||
- <%= prodPort %><% if (filters.mongoose) { %>,
+ port: process.env.OPENSHIFT_NODEJS_PORT
+ || process.env.port
+ || <%= prodPort %><% if(filters.mongoose) { %>,
// MongoDB connection options
mongo: {
- uri: process.env.MONGODB_URI ||
- process.env.MONGOHQ_URL ||
- process.env.OPENSHIFT_MONGODB_DB_URL +
- process.env.OPENSHIFT_APP_NAME ||
- 'mongodb://localhost/<%= lodash.slugify(appname) %>'
+ uri: process.env.MONGODB_URI
+ || process.env.MONGOHQ_URL
+ || process.env.OPENSHIFT_MONGODB_DB_URL + process.env.OPENSHIFT_APP_NAME
+ || 'mongodb://localhost/<%= lodash.slugify(appname) %>'
}<% } if (filters.sequelize) { %>,
sequelize: {
- uri: process.env.SEQUELIZE_URI ||
- 'sqlite://',
+ uri: process.env.SEQUELIZE_URI
+ || 'sqlite://',
options: {
logging: false,
storage: 'dist.sqlite',
diff --git a/templates/app/server/config/environment/test.js b/templates/app/server/config/environment/test.js
index ea7d65096..e336d4347 100644
--- a/templates/app/server/config/environment/test.js
+++ b/templates/app/server/config/environment/test.js
@@ -1,4 +1,5 @@
'use strict';
+/*eslint no-process-env:0*/
// Test specific configuration
// ===========================
diff --git a/templates/app/server/config/express.js b/templates/app/server/config/express.js
index 2e4b8b1cb..993fb7a8d 100644
--- a/templates/app/server/config/express.js
+++ b/templates/app/server/config/express.js
@@ -13,10 +13,11 @@ import methodOverride from 'method-override';
import cookieParser from 'cookie-parser';
import errorHandler from 'errorhandler';
import path from 'path';
-import lusca from 'lusca';
+<%_ if(!filters.noModels) { -%>
+import lusca from 'lusca';<% } %>
import config from './environment';<% if (filters.auth) { %>
-import passport from 'passport';<% } %>
-import session from 'express-session';<% if (filters.mongoose) { %>
+import passport from 'passport';<% } %><% if(!filters.noModels) { %>
+import session from 'express-session';<% } %><% if (filters.mongoose) { %>
<%_ if(semver.satisfies(nodeVersion, '>= 4')) { _%>
import connectMongo from 'connect-mongo';<% } else { _%>
import connectMongo from 'connect-mongo/es5';<% } %>
@@ -25,6 +26,9 @@ var MongoStore = connectMongo(session);<% } else if(filters.sequelize) { %>
import sqldb from '../sqldb';
import expressSequelizeSession from 'express-sequelize-session';
var Store = expressSequelizeSession(session.Store);<% } %>
+import stripAnsi from 'strip-ansi';
+
+var browserSync = require('browser-sync').create();
export default function(app) {
var env = app.get('env');
@@ -52,6 +56,7 @@ export default function(app) {
app.use(cookieParser());<% if (filters.auth) { %>
app.use(passport.initialize());<% } %>
+ <% if(!filters.noModels) { %>
// Persist sessions with MongoStore / sequelizeStore
// We need to enable sessions for passport-twitter because it's an
// oauth 1.0 strategy, and Lusca depends on sessions
@@ -83,16 +88,52 @@ export default function(app) {
},
xssProtection: true
}));
- }
+ }<% } %>
if ('development' === env) {
- app.use(require('connect-livereload')({
- ignore: [
- /^\/api\/(.*)/,
- /\.js(\?.*)?$/, /\.css(\?.*)?$/, /\.svg(\?.*)?$/, /\.ico(\?.*)?$/, /\.woff(\?.*)?$/,
- /\.png(\?.*)?$/, /\.jpg(\?.*)?$/, /\.jpeg(\?.*)?$/, /\.gif(\?.*)?$/, /\.pdf(\?.*)?$/
- ]
- }));
+ const webpackDevMiddleware = require('webpack-dev-middleware');
+ const webpack = require('webpack');
+ const makeWebpackConfig = require('../../webpack.make');
+ const webpackConfig = makeWebpackConfig({ DEV: true });
+ const compiler = webpack(webpackConfig);
+
+ /**
+ * Run Browsersync and use middleware for Hot Module Replacement
+ */
+ browserSync.init({
+ open: false,
+ logFileChanges: false,
+ proxy: 'localhost:' + config.port,
+ ws: true,
+ middleware: [
+ webpackDevMiddleware(compiler, {
+ noInfo: false,
+ stats: {
+ colors: true,
+ timings: true,
+ chunks: false
+ }
+ })
+ ],
+ port: config.browserSyncPort,
+ plugins: ['bs-fullscreen-message']
+ });
+
+ /**
+ * Reload all devices when bundle is complete
+ * or send a fullscreen error message to the browser instead
+ */
+ compiler.plugin('done', function (stats) {
+ console.log('webpack done hook');
+ if (stats.hasErrors() || stats.hasWarnings()) {
+ return browserSync.sockets.emit('fullscreen:message', {
+ title: "Webpack Error:",
+ body: stripAnsi(stats.toString()),
+ timeout: 100000
+ });
+ }
+ browserSync.reload();
+ });
}
if ('development' === env || 'test' === env) {
diff --git a/templates/app/server/config/seed(models).js b/templates/app/server/config/seed(models).js
index 24faf233f..07d9d52b8 100644
--- a/templates/app/server/config/seed(models).js
+++ b/templates/app/server/config/seed(models).js
@@ -19,8 +19,8 @@ var User = sqldb.User;<% } %><% } %>
<% if (filters.mongooseModels) { %>Thing.create({<% }
if (filters.sequelizeModels) { %>Thing.bulkCreate([{<% } %>
name: 'Development Tools',
- info: 'Integration with popular tools such as Bower, Grunt, Babel, Karma, ' +
- 'Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, ' +
+ info: 'Integration with popular tools such as Webpack, Gulp, Babel, TypeScript, Karma, ' +
+ 'Mocha, ESLint, Node Inspector, Livereload, Protractor, Pug, ' +
'Stylus, Sass, and Less.'
}, {
name: 'Server and Client integration',
diff --git a/templates/app/spec.js b/templates/app/spec.js
new file mode 100644
index 000000000..614d79d8b
--- /dev/null
+++ b/templates/app/spec.js
@@ -0,0 +1,14 @@
+'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\.<%= scriptExt %>$/);
+testsContext.keys().forEach(testsContext);
diff --git a/templates/app/tsconfig.client(ts).json b/templates/app/tsconfig.client(ts).json
index e84a3e2fe..840028515 100644
--- a/templates/app/tsconfig.client(ts).json
+++ b/templates/app/tsconfig.client(ts).json
@@ -2,41 +2,22 @@
"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"
+ "typings/browser.d.ts"
],
- "files": [
- "client/app/account/account.ts",
- "client/app/account/login/login.controller.ts",
- "client/app/account/settings/settings.controller.ts",
- "client/app/account/signup/signup.controller.ts",
- "client/app/admin/admin.controller.ts",
- "client/app/admin/admin.module.ts",
- "client/app/admin/admin.router.ts",
- "client/app/app.ts",
- "client/app/main/main.controller.ts",
- "client/app/main/main.ts",
- "client/components/auth/auth.module.ts",
- "client/components/auth/auth.service.ts",
- "client/components/auth/interceptor.service.ts",
- "client/components/auth/router.decorator.ts",
- "client/components/auth/user.service.ts",
- "client/components/footer/footer.directive.ts",
- "client/components/modal/modal.service.ts",
- "client/components/mongoose-error/mongoose-error.directive.ts",
- "client/components/navbar/navbar.controller.ts",
- "client/components/navbar/navbar.directive.ts",
- "client/components/oauth-buttons/oauth-buttons.controller.ts",
- "client/components/oauth-buttons/oauth-buttons.directive.ts",
- "client/components/socket/socket.mock.ts",
- "client/components/socket/socket.service.ts",
- "client/components/ui-router/ui-router.mock.ts",
- "client/components/util/util.module.ts",
- "client/components/util/util.service.ts",
- "typings/main.d.ts"
- ]
+ "awesomeTypescriptLoaderOptions": {
+ "resolveGlobs": true,
+ "forkChecker": true
+ }
}
diff --git a/templates/app/tsconfig.client.test(ts).json b/templates/app/tsconfig.client.test(ts).json
index 8263986dd..dea7eba66 100644
--- a/templates/app/tsconfig.client.test(ts).json
+++ b/templates/app/tsconfig.client.test(ts).json
@@ -2,27 +2,11 @@
"compilerOptions": {
"sourceMap": true,
"rootDir": "./client",
+ "module": "commonjs",
"outDir": ".tmp/test"
},
"filesGlob": [
"client/{app,components}/**/*.{spec,mock}.ts",
"client/test_typings/**/*.d.ts"
- ],
- "files": [
- "client/app/main/main.controller.spec.ts",
- "client/components/oauth-buttons/oauth-buttons.controller.spec.ts",
- "client/components/oauth-buttons/oauth-buttons.directive.spec.ts",
- "client/components/socket/socket.mock.ts",
- <%_ if(filters.uirouter) { _%>
- "client/components/ui-router/ui-router.mock.ts",<% } %>
- "client/test_typings/angular-protractor/angular-protractor.d.ts",
- "client/test_typings/selenium-webdriver/selenium-webdriver.d.ts",<% if(filters.mocha) { %>
- "client/test_typings/mocha/mocha.d.ts",
- "client/test_typings/chai/chai.d.ts",
- "client/test_typings/assertion-error/assertion-error.d.ts",
- "client/test_typings/sinon/sinon.d.ts",
- "client/test_typings/sinon-chai/sinon-chai.d.ts",<% } %><% if(filters.jasmine) { %>
- "client/test_typings/jasmine/jasmine.d.ts",<% } %>
- "client/test_typings/tsd.d.ts"
]
}
diff --git a/templates/app/typings(ts).json b/templates/app/typings(ts).json
index cfbd0f707..6ec1e1e7a 100644
--- a/templates/app/typings(ts).json
+++ b/templates/app/typings(ts).json
@@ -1,9 +1,13 @@
{
"ambientDependencies": {
"angular": "registry:dt/angular#1.5.0+20160412133217",
+ "angular-resource": "registry:dt/angular-resource#1.5.0+20160613142217",
"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..393e4c961
--- /dev/null
+++ b/templates/app/webpack.make.js
@@ -0,0 +1,411 @@
+'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
+ };<% } %>
+
+ <%_ if(filters.babel) { -%>
+ config.babel = {
+ shouldPrintComment(commentContents) {
+ <%_ if(filters.flow) { -%>
+ let regex = DEV
+ // keep `// @flow`, `/*@ngInject*/`, & flow type comments in dev
+ ? /(@flow|@ngInject|^:)/
+ // keep `/*@ngInject*/`
+ : /@ngInject/;
+ return regex.test(commentContents);
+ <%_ } -%>
+ <%_ if(!filters.flow) { -%>
+ // keep `/*@ngInject*/`
+ return /@ngInject/.test(commentContents);
+ <%_ } -%>
+ }
+ }<% } %>
+
+ // 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',
+ 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',
+ 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.jade) { _%>
+ // Pug HTML LOADER
+ // Reference: https://github.com/willyelm/pug-html-loader
+ // Allow loading Pug throw js
+ test: /\.(jade|pug)$/,
+ loaders: ['pug-html']
+ }, {<% } %>
+ <%_ if(filters.html) { _%>
+ // 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/bootstrap/less/*.less'),
+ 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/bootstrap-styl/bootstrap/*.styl'),
+ 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',
+ filename: '../client/index.html',
+ alwaysWriteToDisk: true
+ }
+ config.plugins.push(
+ new HtmlWebpackPlugin(htmlConfig),
+ 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/templates/endpoint/basename.controller.js b/templates/endpoint/basename.controller.js
index 9f8ebb0b3..364879ecf 100644
--- a/templates/endpoint/basename.controller.js
+++ b/templates/endpoint/basename.controller.js
@@ -1,45 +1,46 @@
/**
* Using Rails-like standard naming convention for endpoints.
- * GET <%= route %> -> index<% if (filters.models) { %>
+ * GET <%= route %> -> index<% if(filters.models) { %>
* POST <%= route %> -> create
* GET <%= route %>/:id -> show
- * PUT <%= route %>/:id -> update
+ * PUT <%= route %>/:id -> upsert
+ * PATCH <%= route %>/:id -> patch
* DELETE <%= route %>/:id -> destroy<% } %>
*/
-'use strict';<% if (filters.models) { %>
+'use strict';<% if(filters.models) { %>
-import _ from 'lodash';<% if (filters.mongooseModels) { %>
-import <%= classedName %> from './<%= basename %>.model';<% } if (filters.sequelizeModels) { %>
+import jsonpatch from 'fast-json-patch';<% if(filters.mongooseModels) { %>
+import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequelizeModels) { %>
import {<%= classedName %>} from '<%= relativeRequire(config.get('registerModelsFile')) %>';<% } %>
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
- if (entity) {
+ if(entity) {
return res.status(statusCode).json(entity);
}
return null;
};
}
-function saveUpdates(updates) {
+function patchUpdates(patches) {
return function(entity) {
- <% if (filters.mongooseModels) { %>var updated = _.merge(entity, updates);
- return updated.save()
- .then(updated => {<% }
- if (filters.sequelizeModels) { %>return entity.updateAttributes(updates)
- .then(updated => {<% } %>
- return updated;
- });
+ try {
+ jsonpatch.apply(entity, patches, /*validate*/ true);
+ } catch(err) {
+ return Promise.reject(err);
+ }
+
+ return entity.save();
};
}
function removeEntity(res) {
return function(entity) {
- if (entity) {
- <% if (filters.mongooseModels) { %>return entity.remove()<% }
- if (filters.sequelizeModels) { %>return entity.destroy()<% } %>
+ if(entity) {
+ <% if(filters.mongooseModels) { %>return entity.remove()<% }
+ if(filters.sequelizeModels) { %>return entity.destroy()<% } %>
.then(() => {
res.status(204).end();
});
@@ -49,7 +50,7 @@ function removeEntity(res) {
function handleEntityNotFound(res) {
return function(entity) {
- if (!entity) {
+ if(!entity) {
res.status(404).end();
return null;
}
@@ -65,18 +66,18 @@ function handleError(res, statusCode) {
}<% } %>
// Gets a list of <%= classedName %>s
-export function index(req, res) {<% if (!filters.models) { %>
+export function index(req, res) {<% if(!filters.models) { %>
res.json([]);<% } else { %>
- <% if (filters.mongooseModels) { %>return <%= classedName %>.find().exec()<% }
- if (filters.sequelizeModels) { %>return <%= classedName %>.findAll()<% } %>
+ <% if(filters.mongooseModels) { %>return <%= classedName %>.find().exec()<% }
+ if(filters.sequelizeModels) { %>return <%= classedName %>.findAll()<% } %>
.then(respondWithResult(res))
.catch(handleError(res));<% } %>
-}<% if (filters.models) { %>
+}<% if(filters.models) { %>
// Gets a single <%= classedName %> from the DB
export function show(req, res) {
- <% if (filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
- if (filters.sequelizeModels) { %>return <%= classedName %>.find({
+ <% if(filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
+ if(filters.sequelizeModels) { %>return <%= classedName %>.find({
where: {
_id: req.params.id
}
@@ -88,33 +89,50 @@ export function show(req, res) {
// Creates a new <%= classedName %> in the DB
export function create(req, res) {
- <% if (filters.mongooseModels) { %>return <%= classedName %>.create(req.body)<% }
- if (filters.sequelizeModels) { %>return <%= classedName %>.create(req.body)<% } %>
+ <% if(filters.mongooseModels) { %>return <%= classedName %>.create(req.body)<% }
+ if(filters.sequelizeModels) { %>return <%= classedName %>.create(req.body)<% } %>
.then(respondWithResult(res, 201))
.catch(handleError(res));
}
+// Upserts the given <%= classedName %> in the DB at the specified ID
+export function upsert(req, res) {
+ if(req.body._id) {
+ delete req.body._id;
+ }
+ <%_ if(filters.mongooseModels) { -%>
+ return <%= classedName %>.findOneAndUpdate(req.params.id, req.body, {upsert: true, setDefaultsOnInsert: true, runValidators: true}).exec()<% } %>
+ <%_ if(filters.sequelizeModels) { -%>
+ return <%= classedName %>.upsert(req.body, {
+ where: {
+ _id: req.params.id
+ }
+ })<% } %>
+ .then(respondWithResult(res))
+ .catch(handleError(res));
+}
+
// Updates an existing <%= classedName %> in the DB
-export function update(req, res) {
- if (req.body._id) {
+export function patch(req, res) {
+ if(req.body._id) {
delete req.body._id;
}
- <% if (filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
- if (filters.sequelizeModels) { %>return <%= classedName %>.find({
+ <% if(filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
+ if(filters.sequelizeModels) { %>return <%= classedName %>.find({
where: {
_id: req.params.id
}
})<% } %>
.then(handleEntityNotFound(res))
- .then(saveUpdates(req.body))
+ .then(patchUpdates(req.body))
.then(respondWithResult(res))
.catch(handleError(res));
}
// Deletes a <%= classedName %> from the DB
export function destroy(req, res) {
- <% if (filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
- if (filters.sequelizeModels) { %>return <%= classedName %>.find({
+ <% if(filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% }
+ if(filters.sequelizeModels) { %>return <%= classedName %>.find({
where: {
_id: req.params.id
}
diff --git a/templates/endpoint/basename.events(models).js b/templates/endpoint/basename.events(models).js
index f50f8c931..374c8108f 100644
--- a/templates/endpoint/basename.events(models).js
+++ b/templates/endpoint/basename.events(models).js
@@ -4,38 +4,41 @@
'use strict';
-import {EventEmitter} from 'events';<% if (filters.mongooseModels) { %>
-import <%= classedName %> from './<%= basename %>.model';<% } if (filters.sequelizeModels) { %>
+import {EventEmitter} from 'events';<% if(filters.mongooseModels) { %>
+import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequelizeModels) { %>
var <%= classedName %> = require('<%= relativeRequire(config.get('registerModelsFile')) %>').<%= classedName %>;<% } %>
var <%= classedName %>Events = new EventEmitter();
// Set max event listeners (0 == unlimited)
<%= classedName %>Events.setMaxListeners(0);
-// Model events<% if (filters.mongooseModels) { %>
+// Model events
+<%_ if(filters.mongooseModels) { -%>
var events = {
- 'save': 'save',
- 'remove': 'remove'
-};<% } if (filters.sequelizeModels) { %>
+ save: 'save',
+ remove: 'remove'
+};
+<%_ } if(filters.sequelizeModels) { -%>
var events = {
- 'afterCreate': 'save',
- 'afterUpdate': 'save',
- 'afterDestroy': 'remove'
-};<% } %>
+ afterCreate: 'save',
+ afterUpdate: 'save',
+ afterDestroy: 'remove'
+};
+<%_ } -%>
// Register the event emitter to the model events
-for (var e in events) {
- var event = events[e];<% if (filters.mongooseModels) { %>
- <%= classedName %>.schema.post(e, emitEvent(event));<% } if (filters.sequelizeModels) { %>
+for(var e in events) {
+ let event = events[e];<% if(filters.mongooseModels) { %>
+ <%= classedName %>.schema.post(e, emitEvent(event));<% } if(filters.sequelizeModels) { %>
<%= classedName %>.hook(e, emitEvent(event));<% } %>
}
function emitEvent(event) {
- return function(doc<% if (filters.sequelizeModels) { %>, options, done<% } %>) {
+ return function(doc<% if(filters.sequelizeModels) { %>, options, done<% } %>) {
<%= classedName %>Events.emit(event + ':' + doc._id, doc);
- <%= classedName %>Events.emit(event, doc);<% if (filters.sequelizeModels) { %>
+ <%= classedName %>Events.emit(event, doc);<% if(filters.sequelizeModels) { %>
done(null);<% } %>
- }
+ };
}
export default <%= classedName %>Events;
diff --git a/templates/endpoint/basename.integration.js b/templates/endpoint/basename.integration.js
index f805e2397..9f5db9738 100644
--- a/templates/endpoint/basename.integration.js
+++ b/templates/endpoint/basename.integration.js
@@ -6,7 +6,6 @@ import request from 'supertest';<% if(filters.models) { %>
var new<%= classedName %>;<% } %>
describe('<%= classedName %> API:', function() {
-
describe('GET <%= route %>', function() {
var <%= cameledName %>s;
@@ -16,7 +15,7 @@ describe('<%= classedName %> API:', function() {
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
- if (err) {
+ if(err) {
return done(err);
}
<%= cameledName %>s = res.body;
@@ -27,7 +26,6 @@ describe('<%= classedName %> API:', function() {
it('should respond with JSON array', function() {
<%= expect() %><%= cameledName %>s<%= to() %>.be.instanceOf(Array);
});
-
});<% if(filters.models) { %>
describe('POST <%= route %>', function() {
@@ -41,7 +39,7 @@ describe('<%= classedName %> API:', function() {
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
- if (err) {
+ if(err) {
return done(err);
}
new<%= classedName %> = res.body;
@@ -53,7 +51,6 @@ describe('<%= classedName %> API:', function() {
<%= expect() %>new<%= classedName %>.name<%= to() %>.equal('New <%= classedName %>');
<%= expect() %>new<%= classedName %>.info<%= to() %>.equal('This is the brand new <%= cameledName %>!!!');
});
-
});
describe('GET <%= route %>/:id', function() {
@@ -61,11 +58,11 @@ describe('<%= classedName %> API:', function() {
beforeEach(function(done) {
request(app)
- .get('<%= route %>/' + new<%= classedName %>._id)
+ .get(`<%= route %>/${new<%= classedName %>._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
- if (err) {
+ if(err) {
return done(err);
}
<%= cameledName %> = res.body;
@@ -81,7 +78,6 @@ describe('<%= classedName %> API:', function() {
<%= expect() %><%= cameledName %>.name<%= to() %>.equal('New <%= classedName %>');
<%= expect() %><%= cameledName %>.info<%= to() %>.equal('This is the brand new <%= cameledName %>!!!');
});
-
});
describe('PUT <%= route %>/:id', function() {
@@ -89,7 +85,7 @@ describe('<%= classedName %> API:', function() {
beforeEach(function(done) {
request(app)
- .put('<%= route %>/' + new<%= classedName %>._id)
+ .put(`<%= route %>/${new<%= classedName %>._id}`)
.send({
name: 'Updated <%= classedName %>',
info: 'This is the updated <%= cameledName %>!!!'
@@ -97,7 +93,7 @@ describe('<%= classedName %> API:', function() {
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
- if (err) {
+ if(err) {
return done(err);
}
updated<%= classedName %> = res.body;
@@ -109,21 +105,68 @@ describe('<%= classedName %> API:', function() {
updated<%= classedName %> = {};
});
- it('should respond with the updated <%= cameledName %>', function() {
- <%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('Updated <%= classedName %>');
- <%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!');
+ it('should respond with the original <%= cameledName %>', function() {
+ <%= expect() %>updated<%= classedName %>.name<%= to() %>.equal('New <%= classedName %>');
+ <%= expect() %>updated<%= classedName %>.info<%= to() %>.equal('This is the brand new <%= cameledName %>!!!');
});
+ it('should respond with the updated <%= cameledName %> on a subsequent GET', function(done) {
+ request(app)
+ .get(`<%= route %>/${new<%= classedName %>._id}`)
+ .expect(200)
+ .expect('Content-Type', /json/)
+ .end((err, res) => {
+ if(err) {
+ return done(err);
+ }
+ let <%= cameledName %> = res.body;
+
+ <%= expect() %><%= cameledName %>.name<%= to() %>.equal('Updated <%= classedName %>');
+ <%= expect() %><%= cameledName %>.info<%= to() %>.equal('This is the updated <%= cameledName %>!!!');
+
+ done();
+ });
+ });
});
- describe('DELETE <%= route %>/:id', function() {
+ describe('PATCH <%= route %>/:id', function() {
+ var patched<%= classedName %>;
+
+ beforeEach(function(done) {
+ request(app)
+ .patch(`<%= route %>/${new<%= classedName %>._id}`)
+ .send([
+ { op: 'replace', path: '/name', value: 'Patched <%= classedName %>' },
+ { op: 'replace', path: '/info', value: 'This is the patched <%= cameledName %>!!!' }
+ ])
+ .expect(200)
+ .expect('Content-Type', /json/)
+ .end(function(err, res) {
+ if(err) {
+ return done(err);
+ }
+ patched<%= classedName %> = res.body;
+ done();
+ });
+ });
+ afterEach(function() {
+ patched<%= classedName %> = {};
+ });
+
+ it('should respond with the patched <%= cameledName %>', function() {
+ <%= expect() %>patched<%= classedName %>.name<%= to() %>.equal('Patched <%= classedName %>');
+ <%= expect() %>patched<%= classedName %>.info<%= to() %>.equal('This is the patched <%= cameledName %>!!!');
+ });
+ });
+
+ describe('DELETE <%= route %>/:id', function() {
it('should respond with 204 on successful removal', function(done) {
request(app)
- .delete('<%= route %>/' + new<%= classedName %>._id)
+ .delete(`<%= route %>/${new<%= classedName %>._id}`)
.expect(204)
- .end((err, res) => {
- if (err) {
+ .end(err => {
+ if(err) {
return done(err);
}
done();
@@ -132,16 +175,14 @@ describe('<%= classedName %> API:', function() {
it('should respond with 404 when <%= cameledName %> does not exist', function(done) {
request(app)
- .delete('<%= route %>/' + new<%= classedName %>._id)
+ .delete(`<%= route %>/${new<%= classedName %>._id}`)
.expect(404)
- .end((err, res) => {
- if (err) {
+ .end(err => {
+ if(err) {
return done(err);
}
done();
});
});
-
});<% } %>
-
});
diff --git a/templates/endpoint/basename.socket(socketio).js b/templates/endpoint/basename.socket(socketio).js
index b9ee3ab56..0662cb886 100644
--- a/templates/endpoint/basename.socket(socketio).js
+++ b/templates/endpoint/basename.socket(socketio).js
@@ -11,9 +11,9 @@ var events = ['save', 'remove'];
export function register(socket) {
// Bind model events to socket events
- for (var i = 0, eventsLength = events.length; i < eventsLength; i++) {
+ for(var i = 0, eventsLength = events.length; i < eventsLength; i++) {
var event = events[i];
- var listener = createListener('<%= cameledName %>:' + event, socket);
+ var listener = createListener(`<%= cameledName %>:${event}`, socket);
<%= classedName %>Events.on(event, listener);
socket.on('disconnect', removeListener(event, listener));
diff --git a/templates/endpoint/index.js b/templates/endpoint/index.js
index 26dc430dd..8db1adc32 100644
--- a/templates/endpoint/index.js
+++ b/templates/endpoint/index.js
@@ -8,8 +8,8 @@ var router = express.Router();
router.get('/', controller.index);<% if (filters.models) { %>
router.get('/:id', controller.show);
router.post('/', controller.create);
-router.put('/:id', controller.update);
-router.patch('/:id', controller.update);
+router.put('/:id', controller.upsert);
+router.patch('/:id', controller.patch);
router.delete('/:id', controller.destroy);<% } %>
module.exports = router;
diff --git a/templates/endpoint/index.spec.js b/templates/endpoint/index.spec.js
index 81b6ec54a..07b343b6a 100644
--- a/templates/endpoint/index.spec.js
+++ b/templates/endpoint/index.spec.js
@@ -6,7 +6,8 @@ var <%= cameledName %>CtrlStub = {
index: '<%= cameledName %>Ctrl.index'<% if(filters.models) { %>,
show: '<%= cameledName %>Ctrl.show',
create: '<%= cameledName %>Ctrl.create',
- update: '<%= cameledName %>Ctrl.update',
+ upsert: '<%= cameledName %>Ctrl.upsert',
+ patch: '<%= cameledName %>Ctrl.patch',
destroy: '<%= cameledName %>Ctrl.destroy'<% } %>
};
@@ -20,8 +21,8 @@ var routerStub = {
// require the index with our stubbed out modules
var <%= cameledName %>Index = proxyquire('./index.js', {
- 'express': {
- Router: function() {
+ express: {
+ Router() {
return routerStub;
}
},
@@ -29,69 +30,55 @@ var <%= cameledName %>Index = proxyquire('./index.js', {
});
describe('<%= classedName %> API Router:', function() {
-
it('should return an express router instance', function() {
<%= expect() %><%= cameledName %>Index<%= to() %>.equal(routerStub);
});
describe('GET <%= route %>', function() {
-
it('should route to <%= cameledName %>.controller.index', function() {
<%= expect() %>routerStub.get
.withArgs('/', '<%= cameledName %>Ctrl.index')
<%= to() %>.have.been.calledOnce;
});
-
});<% if(filters.models) { %>
describe('GET <%= route %>/:id', function() {
-
it('should route to <%= cameledName %>.controller.show', function() {
<%= expect() %>routerStub.get
.withArgs('/:id', '<%= cameledName %>Ctrl.show')
<%= to() %>.have.been.calledOnce;
});
-
});
describe('POST <%= route %>', function() {
-
it('should route to <%= cameledName %>.controller.create', function() {
<%= expect() %>routerStub.post
.withArgs('/', '<%= cameledName %>Ctrl.create')
<%= to() %>.have.been.calledOnce;
});
-
});
describe('PUT <%= route %>/:id', function() {
-
- it('should route to <%= cameledName %>.controller.update', function() {
+ it('should route to <%= cameledName %>.controller.upsert', function() {
<%= expect() %>routerStub.put
- .withArgs('/:id', '<%= cameledName %>Ctrl.update')
+ .withArgs('/:id', '<%= cameledName %>Ctrl.upsert')
<%= to() %>.have.been.calledOnce;
});
-
});
describe('PATCH <%= route %>/:id', function() {
-
- it('should route to <%= cameledName %>.controller.update', function() {
+ it('should route to <%= cameledName %>.controller.patch', function() {
<%= expect() %>routerStub.patch
- .withArgs('/:id', '<%= cameledName %>Ctrl.update')
+ .withArgs('/:id', '<%= cameledName %>Ctrl.patch')
<%= to() %>.have.been.calledOnce;
});
-
});
describe('DELETE <%= route %>/:id', function() {
-
it('should route to <%= cameledName %>.controller.destroy', function() {
<%= expect() %>routerStub.delete
.withArgs('/:id', '<%= cameledName %>Ctrl.destroy')
<%= to() %>.have.been.calledOnce;
});
-
});<% } %>
-
});
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..b6bc72cce 100644
--- a/test/fixtures/.yo-rc.json
+++ b/test/fixtures/.yo-rc.json
@@ -26,7 +26,6 @@
"models": true,
"mongooseModels": true,
"mongoose": true,
- "grunt": true,
"mocha": true,
"jasmine": false,
"expect": true