diff --git a/templates/app/.eslintrc b/templates/app/.eslintrc index 267f4f7f0..93916084e 100644 --- a/templates/app/.eslintrc +++ b/templates/app/.eslintrc @@ -224,7 +224,7 @@ //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-parens": 0, //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 diff --git a/templates/app/_package.json b/templates/app/_package.json index 835e44b8e..deace0887 100644 --- a/templates/app/_package.json +++ b/templates/app/_package.json @@ -36,7 +36,7 @@ "sequelize": "^3.23.6", "sqlite3": "~3.1.1", "connect-session-sequelize": "^4.1.0",<% } %><% if(filters.auth) { %> - "jsonwebtoken": "^7.0.0", + "jsonwebtoken": "^8.3.0", "express-jwt": "^5.0.0", "passport": "~0.4.0", "passport-local": "^1.0.0",<% } %><% if(filters.facebookAuth) { %> @@ -52,16 +52,15 @@ <%# CLIENT %> "@angularclass/hmr-loader": "^3.0.4", "reflect-metadata": "^0.1.3", - "rxjs": "^5.5.2", + "rxjs": "^6.0.0", "zone.js": "^0.8.12", - "@angular/common": "^5.0.1", - "@angular/compiler": "^5.0.1", - "@angular/core": "^5.0.1", - "@angular/forms": "^5.0.1", - "@angular/http": "^5.0.1", - "@angular/platform-browser": "^5.0.1", - "@angular/platform-browser-dynamic": "^5.0.1", - "@angular/router": "^5.0.1", + "@angular/common": "^6.0.4", + "@angular/compiler": "^6.0.4", + "@angular/core": "^6.0.4", + "@angular/forms": "^6.0.4", + "@angular/platform-browser": "^6.0.4", + "@angular/platform-browser-dynamic": "^6.0.4", + "@angular/router": "^6.0.4", <%#"@angular/material": "5.0.0-rc0",%> "@angularclass/hmr": "^2.1.3", <%_ if(filters.ts) { -%> @@ -80,7 +79,7 @@ "@types/selenium-webdriver": "^3.0.3", "@types/webpack": "^3.0.10", <%_ } -%> - "angular2-jwt": "^0.2.3", + "@auth0/angular-jwt": "^2.0.0", <% if(filters.auth) { %> "angular-validation-match": "^1.9.0",<% } %> <% if(filters.uirouter) { %> @@ -89,7 +88,7 @@ <% if(filters.bootstrap) { %> "bootstrap": "~3.3.7", <% if(filters.uibootstrap) { %> - "ngx-bootstrap": "~1.6.3",<% } %> + "ngx-bootstrap": "^3.0.0",<% } %> <% if(filters.sass) { %> "bootstrap-sass": "~3.3.7",<% } %> <% if(filters.stylus) { %> @@ -106,7 +105,7 @@ <%# END CLIENT %> "autoprefixer": "^7.1.3", - "babel-eslint": "^7.2.3", + "babel-eslint": "^8.2.3", "babel-register": "^6.16.0", "bs-fullscreen-message": "^1.0.0", <%_ if(filters.flow) { -%> @@ -117,7 +116,7 @@ "babel-plugin-istanbul": "^4.1.4", "babel-preset-env": "^1.6.1", "cross-env": "^5.1.1", - "eslint": "^2.12.0", + "eslint": "^4.19.1", "del": "^3.0.0", "gulp": "^3.9.1", "gulp-babel": "^7.0.0",<% if(filters.ts) { %> @@ -148,11 +147,12 @@ "run-sequence": "^2.1.0", "lazypipe": "^1.0.1", <%# WEBPACK %> - "webpack": "^3.5.5", - "webpack-dev-server": "^2.4.2", - "extract-text-webpack-plugin": "3.0.0", - "html-webpack-plugin": "^2.24.1", - "html-webpack-harddisk-plugin": "~0.1.0", + "webpack": "^4.12.0", + "webpack-cli": "^3.0.6", + "webpack-dev-server": "^3.1.4", + "extract-text-webpack-plugin": "4.0.0-beta.0", + "html-webpack-plugin": "^3.2.0", + "html-webpack-harddisk-plugin": "~0.2.0", <%_ if(filters.pug) { _%> "pug-html-loader": "^1.1.5",<% } %> "typescript": "~2.6.1", @@ -175,7 +175,7 @@ <%_ if(filters.stylus) { _%> "stylus": "^0.54.5", "stylus-loader": "^3.0.1",<% } %> - "karma-webpack": "^2.0.3", + "karma-webpack": "4.0.0-beta.0", "to-string-loader": "^1.1.5", <%# END WEBPACK %> "through2": "^2.0.1", diff --git a/templates/app/client/app/app.module.js b/templates/app/client/app/app.module.js index e14e834f4..2e95297d7 100644 --- a/templates/app/client/app/app.module.js +++ b/templates/app/client/app/app.module.js @@ -1,17 +1,9 @@ import { NgModule, - Injectable, - ApplicationRef,<% if(filters.ts || filters.flow) { %> - Provider,<% } %> + ApplicationRef, } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { - Http, - HttpModule, - BaseRequestOptions,<% if(filters.ts || filters.flow) { %> - RequestOptions, - RequestOptionsArgs,<% } %> -} from '@angular/http'; +import { HttpClientModule } from '@angular/common/http'; import { removeNgStyles, createNewHosts, @@ -21,40 +13,16 @@ import { import { UIRouterModule } from 'ui-router-ng2';<% } %> <%_ if (filters.ngroute) { -%> import { RouterModule, Routes } from '@angular/router';<% } %> -import { AuthHttp, AuthConfig } from 'angular2-jwt'; import { AppComponent } from './app.component'; import { MainModule } from './main/main.module'; import { DirectivesModule } from '../components/directives.module';<% if(filters.auth) { %> +import { JwtModule } from '@auth0/angular-jwt'; import { AccountModule } from './account/account.module'; import { AdminModule } from './admin/admin.module';<% } %> -import constants from './app.constants'; - -export function getAuthHttp(http) { - return new AuthHttp(new AuthConfig({ - noJwtError: true, - globalHeaders: [{'Accept': 'application/json'}], - tokenGetter: (() => localStorage.getItem('id_token')), - }), http); -} - -let providers: Provider[] = [{ - provide: AuthHttp, - useFactory: getAuthHttp, - deps: [Http] -}]; - -if(constants.env === 'development') { - @Injectable() - class HttpOptions extends BaseRequestOptions { - merge(options: RequestOptionsArgs): RequestOptions { - options.url = `http://localhost:9000${options.url}`; - return super.merge(options); - } - } - - providers.push({ provide: RequestOptions, useClass: HttpOptions }); +export function tokenGetter() { + return localStorage.getItem('id_token'); } const appRoutes: Routes = [{ path: '', @@ -63,10 +31,14 @@ const appRoutes: Routes = [{ path: '', }]; @NgModule({ - providers, imports: [ BrowserModule, - HttpModule, + HttpClientModule,<% if(filters.auth) { %> + JwtModule.forRoot({ + config: { + tokenGetter, + } + }),<% } %> <%_ if (filters.uirouter) { -%> UIRouterModule.forRoot(),<% } %> <%_ if (filters.ngroute) { -%> @@ -79,7 +51,7 @@ const appRoutes: Routes = [{ path: '', declarations: [ AppComponent, ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule { static parameters = [ApplicationRef]; diff --git a/templates/app/client/app/main/main.component.js b/templates/app/client/app/main/main.component.js index c0a3bf49b..dde4daf50 100644 --- a/templates/app/client/app/main/main.component.js +++ b/templates/app/client/app/main/main.component.js @@ -1,8 +1,15 @@ import { Component, OnInit<% if(filters.ws) { %>, OnDestroy<% } %> } from '@angular/core'; -import { Http } from '@angular/http'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map';<% if(filters.ws) { %> -import { SocketService } from '../../components/socket/socket.service';<% } %> +import { HttpClient } from '@angular/common/http';<% if(filters.ws) { %> +import { SocketService } from '../../components/socket/socket.service';<% } %><% if(filters.ts) { %> + +interface Thing { + name: string; + info?: string; +}<% } %><% if(filters.flow) { %> +type Thing = { + name: string; + info?: string; +};<% } %> @Component({ selector: 'main', @@ -10,27 +17,23 @@ import { SocketService } from '../../components/socket/socket.service';<% } %> styles: [require('./main.<%=styleExt%>')], }) export class MainComponent implements OnInit<% if(filters.ws) { %>, OnDestroy<% } %> { - Http; <%_ if(filters.ws) { -%> SocketService;<% } %> - awesomeThings = []; + awesomeThings: Thing[] = []; <%_ if(filters.models) { -%> newThing = '';<% } %> - static parameters = [Http<% if(filters.ws) { %>, SocketService<% } %>]; - constructor(<%= private() %>http: Http<% if(filters.ws) { %>, <%= private() %>socketService: SocketService<% } %>) { - this.Http = http; + static parameters = [HttpClient<% if(filters.ws) { %>, SocketService<% } %>]; + constructor(<%= private() %>http: HttpClient<% if(filters.ws) { %>, <%= private() %>socketService: SocketService<% } %>) { + this.http = http; <%_ if(filters.ws) { -%> this.SocketService = socketService;<% } %> } ngOnInit() { - return this.Http.get('/api/things') - .map(res => res.json()) - // .catch(err => Observable.throw(err.json().error || 'Server error')) - .subscribe(things => { - this.awesomeThings = things; - <%_ if(filters.ws) { -%> + return this.http.get('/api/things') + .subscribe((things: Thing[]) => { + this.awesomeThings = things;<% if(filters.ws) { %> this.SocketService.syncUpdates('thing', this.awesomeThings);<% } %> }); }<% if (filters.models) { %> @@ -45,9 +48,7 @@ export class MainComponent implements OnInit<% if(filters.ws) { %>, OnDestroy<% let text = this.newThing; this.newThing = ''; - return this.Http.post('/api/things', { name: text }) - .map(res => res.json()) - .catch(err => Observable.throw(err.json().error || 'Server error')) + return this.http.post('/api/things', { name: text }) .subscribe(thing => { console.log('Added Thing:', thing); }); @@ -55,9 +56,7 @@ export class MainComponent implements OnInit<% if(filters.ws) { %>, OnDestroy<% } deleteThing(thing) { - return this.Http.delete(`/api/things/${thing._id}`) - .map(res => res.json()) - .catch(err => Observable.throw(err.json().error || 'Server error')) + return this.http.delete(`/api/things/${thing._id}`) .subscribe(() => { console.log('Deleted Thing'); }); diff --git a/templates/app/client/app/main/main.component.spec.js b/templates/app/client/app/main/main.component.spec.js index 60397b537..4f4703997 100644 --- a/templates/app/client/app/main/main.component.spec.js +++ b/templates/app/client/app/main/main.component.spec.js @@ -5,16 +5,7 @@ import { inject, TestBed, } from '@angular/core/testing'; -import { - BaseRequestOptions, - ConnectionBackend, - Http, - HttpModule, - Response, - ResponseOptions, -} from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; -<%_ if(filters.mocha && filters.expect) { -%> +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';<% if(filters.mocha && filters.expect) { %> import { expect } from 'chai';<% } %><% if(filters.uibootstrap) { %> import { TooltipModule } from 'ngx-bootstrap';<% } %> import { FormsModule } from '@angular/forms';<% if(filters.ws) { %> @@ -25,37 +16,24 @@ import { MainComponent } from './main.component'; describe('Component: MainComponent', function() { let comp: MainComponent; let fixture: ComponentFixture; + let httpTestingController: HttpTestingController; + const mockThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ FormsModule,<% if(filters.uibootstrap) { %> TooltipModule.forRoot(),<% } %> - HttpModule, + HttpClientTestingModule, ], - declarations: [ MainComponent ], // declare the test component + declarations: [ MainComponent ], // declare the test component<% if(filters.ws) { %> providers: [ - BaseRequestOptions, - MockBackend, - { - provide: Http, - useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => { - return new Http(backend, defaultOptions); - }, - deps: [MockBackend, BaseRequestOptions] - },<% if(filters.ws) { %> - { provide: SocketService, useClass: SocketServiceStub },<% } %> - ], + { provide: SocketService, useClass: SocketServiceStub }, + ],<% } %> }).compileComponents(); - })); - beforeEach(async(inject([MockBackend], (mockBackend) => { - mockBackend.connections.subscribe(conn => { - conn.mockRespond(new Response(new ResponseOptions({ - body: JSON.stringify(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']) - }))); - }); - }))); + httpTestingController = TestBed.get(HttpTestingController); + })); beforeEach(async(() => { fixture = TestBed.createComponent(MainComponent); @@ -63,13 +41,24 @@ describe('Component: MainComponent', function() { comp = fixture.componentInstance; /** - * Trigger initial data binding. + * Trigger initial data binding and run lifecycle hooks */ fixture.detectChanges(); })); - it('should attach a list of things to the controller', () => {<% if(filters.jasmine) { %> - expect(comp.awesomeThings.length).toEqual(4);<% } else if(filters.mocha) { %> - <%= expect() %>comp.awesomeThings.length<%= to() %>.equal(4);<% } %> + it('should attach a list of things to the controller', () => { + // `GET /api/things` should be made once + const req = httpTestingController.expectOne('/api/things');<% if(filters.jasmine) { %> + expect(req.request.method).toEqual('GET');<% } else if(filters.mocha) { %> + <%= expect() %>req.request.method<%= to() %>.equal('GET');<% } %> + + // Respond with mock data + req.flush(mockThings); + + // assert that there are no outstanding requests + httpTestingController.verify(); + + <%_ if(filters.jasmine) { -%>expect(comp.awesomeThings).toEqual(mockThings);<%_ } else if(filters.mocha) { -%> + <%= expect() %>comp.awesomeThings<%= to() %>.equal(mockThings);<% } %> }); }); diff --git a/templates/app/client/app/polyfills.js b/templates/app/client/app/polyfills.js index 3ffde387a..0a6d8ca49 100644 --- a/templates/app/client/app/polyfills.js +++ b/templates/app/client/app/polyfills.js @@ -1,5 +1,7 @@ -import 'core-js/es6'; -import 'core-js/es7/reflect'; +// Enable certain polyfills depending on which browsers you need to support +// import 'core-js/es6'; +// import 'core-js/es7/reflect'; + import 'zone.js/dist/zone'; <%_ if(filters.ts) { -%> diff --git a/templates/app/client/components/auth(auth)/auth.service.js b/templates/app/client/components/auth(auth)/auth.service.js index 477810d64..f5b6c7b9e 100644 --- a/templates/app/client/components/auth(auth)/auth.service.js +++ b/templates/app/client/components/auth(auth)/auth.service.js @@ -1,9 +1,7 @@ import { Injectable, EventEmitter, Output } from '@angular/core'; -import { AuthHttp } from 'angular2-jwt'; import { UserService } from './user.service'; -import { Http } from '@angular/http'; -import 'rxjs/add/operator/toPromise'; -import { safeCb, extractData } from '../util'; +import { HttpClient } from '@angular/common/http'; +import { safeCb } from '../util'; import constants from '../../app/app.constants'; // @flow @@ -12,7 +10,6 @@ class User { name = ''; email = ''; role = ''; - $promise = undefined; } @Injectable() @@ -20,14 +17,11 @@ export class AuthService { _currentUser: User = new User(); @Output() currentUserChanged = new EventEmitter(true); userRoles = constants.userRoles || []; - Http; - AuthHttp; UserService; - static parameters = [Http, AuthHttp, UserService]; - constructor(<%= private() %>http: Http, <%= private() %>authHttp: AuthHttp, <%= private() %>userService: UserService) { - this.Http = http; - this.AuthHttp = authHttp; + static parameters = [HttpClient, UserService]; + constructor(<%= private() %>http: HttpClient, <%= private() %>userService: UserService) { + this.http = http; this.UserService = userService; if(localStorage.getItem('id_token')) { @@ -69,13 +63,12 @@ export class AuthService { * @return {Promise} */ login({email, password}, callback) { - return this.Http.post('/auth/local', { + return this.http.post('/auth/local', { email, password }) .toPromise() - .then(extractData) - .then(res => { + .then((res: {token: string}) => { localStorage.setItem('id_token', res.token); return this.UserService.get().toPromise(); }) diff --git a/templates/app/client/components/auth(auth)/user.service.js b/templates/app/client/components/auth(auth)/user.service.js index 2fea4287a..ac8d03fa3 100644 --- a/templates/app/client/components/auth(auth)/user.service.js +++ b/templates/app/client/components/auth(auth)/user.service.js @@ -1,11 +1,8 @@ // @flow import { Injectable } from '@angular/core'; -import { Response } from '@angular/http'; -import { AuthHttp } from 'angular2-jwt'; -import { Observable } from 'rxjs/Rx'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/toPromise'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; type UserType = { // TODO: use Mongoose model @@ -15,42 +12,27 @@ type UserType = { email?: string; }; -function handleError(err) { - return Observable.throw(err.json() || 'Server error'); -} - @Injectable() export class UserService { - AuthHttp; - - static parameters = [AuthHttp]; - constructor(<%= private() %>authHttp: AuthHttp) { - this.AuthHttp = authHttp; + static parameters = [HttpClient]; + constructor(<%= private() %>http: HttpClient) { + this.http = http; } query(): Observable { - return this.AuthHttp.get('/api/users/') - .map((res: Response) => res.json()) - .catch(handleError); + return this.http.get('/api/users/')<% if(filters.ts) { %> as Observable<% } %>; } get(user<% if(filters.ts) { %>: UserType<% } %> = {id: 'me'}): Observable { - return this.AuthHttp.get(`/api/users/${user.id || user._id}`) - .map((res: Response) => res.json()) - .catch(handleError); + return this.http.get(`/api/users/${user.id || user._id}`)<% if(filters.ts) { %> as Observable<% } %>; } create(user: UserType) { - return this.AuthHttp.post('/api/users/', user) - .map((res: Response) => res.json()) - .catch(handleError); + return this.http.post('/api/users/', user); } changePassword(user, oldPassword, newPassword) { - return this.AuthHttp.put(`/api/users/${user.id || user._id}/password`, {oldPassword, newPassword}) - .map((res: Response) => res.json()) - .catch(handleError); + return this.http.put(`/api/users/${user.id || user._id}/password`, {oldPassword, newPassword}); } remove(user) { - return this.AuthHttp.delete(`/api/users/${user.id || user._id}`) - .map(() => user) - .catch(handleError); + return this.http.delete(`/api/users/${user.id || user._id}`) + .pipe(map(() => user)); } } diff --git a/templates/app/client/components/util.js b/templates/app/client/components/util.js index 723164001..0a4da1766 100644 --- a/templates/app/client/components/util.js +++ b/templates/app/client/components/util.js @@ -6,7 +6,6 @@ import { isFunction, noop, } from 'lodash'; -import { Response } from '@angular/http'; /** * Return a callback or noop function @@ -60,8 +59,3 @@ export function isSameOrigin(url, origins) { }); return origins.length >= 1; } - -export function extractData(res: Response) { - if(!res.text()) return {}; - return res.json() || { }; -} diff --git a/templates/app/client/tslint.json(ts) b/templates/app/client/tslint.json(ts) index 4495b1571..a9a86be10 100644 --- a/templates/app/client/tslint.json(ts) +++ b/templates/app/client/tslint.json(ts) @@ -6,7 +6,6 @@ "forin": true, "indent": [true, "spaces"], "label-position": true, - "label-undefined": true, "max-line-length": [true, 140], "no-arg": true, "no-bitwise": true, @@ -19,7 +18,6 @@ ], "no-construct": true, "no-debugger": true, - "no-duplicate-key": true, "no-duplicate-variable": true, "no-eval": true, "no-inferrable-types": true, 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 df5525110..6584e9d43 100644 --- a/templates/app/server/api/user(auth)/user.model(mongooseModels).js +++ b/templates/app/server/api/user(auth)/user.model(mongooseModels).js @@ -98,28 +98,27 @@ UserSchema // Validate email is not taken UserSchema - .path('email') - .validate(function(value) { - <%_ if(filters.oauth) { -%> - if(authTypes.indexOf(this.provider) !== -1) { - return true; - } - - <%_ } -%> - return this.constructor.findOne({ email: value }).exec() - .then(user => { - if(user) { - if(this.id === user.id) { + .path('email') + .validate(function(value) {<% if(filters.oauth) { %> + if(authTypes.indexOf(this.provider) !== -1) { return true; - } - return false; } - return true; - }) - .catch(function(err) { - throw err; - }); - }, 'The specified email address is already in use.'); + + <%_ } -%> + return this.constructor.findOne({ email: value }).exec() + .then(user => { + if(user) { + if(this.id === user.id) { + return true; + } + return false; + } + return true; + }) + .catch(err => { + throw err; + }); + }, 'The specified email address is already in use.'); var validatePresenceOf = function(value) { return value && value.length; 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 79ca48f2c..413769463 100644 --- a/templates/app/server/api/user(auth)/user.model(sequelizeModels).js +++ b/templates/app/server/api/user(auth)/user.model(sequelizeModels).js @@ -1,5 +1,3 @@ -'use strict'; - import crypto from 'crypto';<% if(filters.oauth) { %> var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> @@ -124,18 +122,18 @@ export default function(sequelize, DataTypes) { }); }, - /** - * Make salt - * - * @param {Number} [byteSize] - Optional salt byte size, default to 16 - * @param {Function} callback - * @return {String} - * @api public - */ - makeSalt(...args) { - let byteSize; - let callback; - let defaultByteSize = 16; + /** + * Make salt + * + * @param {Number} [byteSize] - Optional salt byte size, default to 16 + * @param {Function} callback + * @return {String} + * @api public + */ + makeSalt(...args) { + let byteSize; + let callback; + let defaultByteSize = 16; if(typeof args[0] === 'function') { callback = args[0]; diff --git a/templates/app/server/app.js b/templates/app/server/app.js index 46ad016f8..18e3951e5 100644 --- a/templates/app/server/app.js +++ b/templates/app/server/app.js @@ -2,8 +2,6 @@ * Main application file */ -'use strict'; - import express from 'express';<% if (filters.mongoose) { %> import mongoose from 'mongoose'; mongoose.Promise = require('bluebird');<% } %><% if (filters.sequelize) { %> @@ -20,8 +18,8 @@ import seedDatabaseIfNeeded from './config/seed';<% } %> // Connect to MongoDB mongoose.connect(config.mongo.uri, config.mongo.options); mongoose.connection.on('error', function(err) { - console.error('MongoDB connection error: ' + err); - process.exit(-1); // eslint-disable-line no-process-exit + console.error('MongoDB connection error: ' + err); + process.exit(-1); // eslint-disable-line no-process-exit }); <% } %> // Setup server @@ -34,33 +32,32 @@ registerRoutes(app); // Start server function startServer() { - app.angularFullstack = server.listen(config.port, config.ip, function() { - console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); - }); + app.angularFullstack = server.listen(config.port, config.ip, function() { + console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); + }); } <% if(filters.sequelize) { %> -sqldb.sequelize.sync() - <%_ if(filters.ws) { -%> - .then(wsInitPromise) - .then(primus => { - app.primus = primus; - })<% } %><% if(filters.models) { %> - .then(seedDatabaseIfNeeded)<% } %> - .then(startServer) - .catch(err => { - console.log('Server failed to start due to error: %s', err); - }); +sqldb.sequelize.sync()<% if(filters.ws) { %> + .then(wsInitPromise) + .then(primus => { + app.primus = primus; + })<% } %><% if(filters.models) { %> + .then(seedDatabaseIfNeeded)<% } %> + .then(startServer) + .catch(err => { + console.log('Server failed to start due to error: %s', err); + }); <% } else { %> <%_ if(filters.ws) { -%> wsInitPromise - .then(primus => { - app.primus = primus; - })<% if(filters.models) { %> - .then(seedDatabaseIfNeeded)<% } %> - .then(startServer) - .catch(err => { - console.log('Server failed to start due to error: %s', err); - });<% } %> + .then(primus => { + app.primus = primus; + })<% if(filters.models) { %> + .then(seedDatabaseIfNeeded)<% } %> + .then(startServer) + .catch(err => { + console.log('Server failed to start due to error: %s', err); + });<% } %> <%_ if(!filters.ws) { -%> setImmediate(startServer);<% } %> <% } %> diff --git a/templates/app/server/config/environment/development.js b/templates/app/server/config/environment/development.js index 894464e23..b79c722f5 100644 --- a/templates/app/server/config/environment/development.js +++ b/templates/app/server/config/environment/development.js @@ -6,10 +6,10 @@ module.exports = {<% if (filters.mongoose) { %> // MongoDB connection options mongo: { - uri: 'mongodb://localhost/<%= lodash.slugify(appname) %>-dev' + uri: process.env.MONGODB_URI || 'mongodb://localhost/<%= lodash.slugify(appname) %>-dev' },<% } if (filters.sequelize) { %> - // Sequelize connection opions + // Sequelize connection options sequelize: { uri: 'sqlite://', options: { @@ -22,5 +22,5 @@ module.exports = {<% if (filters.mongoose) { %> },<% } %> // Seed database on startup - seedDB: true + seedDB: true, }; diff --git a/templates/app/server/config/seed(models).js b/templates/app/server/config/seed(models).js index 17ad9210e..e9c105e55 100644 --- a/templates/app/server/config/seed(models).js +++ b/templates/app/server/config/seed(models).js @@ -2,74 +2,73 @@ * Populate DB with sample data on server start * to disable, edit config/environment/index.js, and set `seedDB: false` */ - -'use strict';<% if(filters.mongooseModels) { %> +<% if(filters.mongooseModels) { %> import Thing from '../api/thing/thing.model';<% if(filters.auth) { %> import User from '../api/user/user.model';<% } %><% } %><% if(filters.sequelizeModels) { %> import sqldb from '../sqldb';<% } %> import config from './environment/'; export default function seedDatabaseIfNeeded() { - if(!config.seedDB) { - return Promise.resolve(); - } + if(!config.seedDB) { + return Promise.resolve(); + } - <% if(filters.sequelizeModels) { %>let Thing = sqldb.Thing;<% if(filters.auth) { %> - let User = sqldb.User;<% } %><% } %> + <% if(filters.sequelizeModels) { %>let Thing = sqldb.Thing;<% if(filters.auth) { %> + let User = sqldb.User;<% } %><% } %> - let promises = []; + let promises = []; - let thingPromise = <% if(filters.mongooseModels) { %>Thing.find({}).remove()<% } if(filters.sequelizeModels) { %>Thing.destroy({ where: {} })<% } %> - .then(() => { - <% if(filters.mongooseModels) { %>return Thing.create({<% } - if(filters.sequelizeModels) { %>return Thing.bulkCreate([{<% } %> - name: 'Development Tools', - info: 'Integration with popular tools such as Webpack, Babel, TypeScript, Karma, Mocha, ESLint, Protractor, ' - + 'Pug, Stylus, Sass, and Less.' - }, { - name: 'Server and Client integration', - info: 'Built with a powerful and fun stack: MongoDB, Express, Angular, and Node.' - }, { - name: 'Smart Build System', - info: 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of ' - + 'scripts and styles into your app.html' - }, { - name: 'Modular Structure', - info: 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { - name: 'Optimized Build', - info: 'Build process packs up your templates as a single JavaScript payload, minifies your ' + - 'scripts/css/images, and rewrites asset names for caching.' - }, { - name: 'Deployment Ready', - info: 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' - <% if(filters.mongooseModels) { %>});<% } - if(filters.sequelizeModels) { %>}]);<% } %> - }) - .then(() => console.log('finished populating things')) - .catch(err => console.log('error populating things', err)); - promises.push(thingPromise); + let thingPromise = <% if(filters.mongooseModels) { %>Thing.find({}).remove()<% } if(filters.sequelizeModels) { %>Thing.destroy({ where: {} })<% } %> + .then(() => { + <% if(filters.mongooseModels) { %>return Thing.create({<% } + if(filters.sequelizeModels) { %>return Thing.bulkCreate([{<% } %> + name: 'Development Tools', + info: 'Integration with popular tools such as Webpack, Babel, TypeScript, Karma, Mocha, ESLint, Protractor, ' + + 'Pug, Stylus, Sass, and Less.' + }, { + name: 'Server and Client integration', + info: 'Built with a powerful and fun stack: MongoDB, Express, Angular, and Node.' + }, { + name: 'Smart Build System', + info: 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of ' + + 'scripts and styles into your app.html' + }, { + name: 'Modular Structure', + info: 'Best practice client and server structures allow for more code reusability and maximum scalability' + }, { + name: 'Optimized Build', + info: 'Build process packs up your templates as a single JavaScript payload, minifies your ' + + 'scripts/css/images, and rewrites asset names for caching.' + }, { + name: 'Deployment Ready', + info: 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' + <% if(filters.mongooseModels) { %>});<% } + if(filters.sequelizeModels) { %>}]);<% } %> + }) + .then(() => console.log('finished populating things')) + .catch(err => console.log('error populating things', err)); + promises.push(thingPromise); <% if(filters.auth) { %> - let userPromise = <% if(filters.mongooseModels) { %>User.find({}).remove()<% } if(filters.sequelizeModels) { %>User.destroy({ where: {} })<% } %> - .then(() => { - <% if(filters.mongooseModels) { %>return User.create({<% } - if(filters.sequelizeModels) { %>return User.bulkCreate([{<% } %> - provider: 'local', - name: 'Test User', - email: 'test@example.com', - password: 'test' - }, { - provider: 'local', - role: 'admin', - name: 'Admin', - email: 'admin@example.com', - password: 'admin' - <% if(filters.mongooseModels) { %>})<% } - if(filters.sequelizeModels) { %>}])<% } %> - .then(() => console.log('finished populating users')) - .catch(err => console.log('error populating users', err)); - }); - promises.push(userPromise);<% } %> + let userPromise = <% if(filters.mongooseModels) { %>User.find({}).remove()<% } if(filters.sequelizeModels) { %>User.destroy({ where: {} })<% } %> + .then(() => { + <% if(filters.mongooseModels) { %>return User.create({<% } + if(filters.sequelizeModels) { %>return User.bulkCreate([{<% } %> + provider: 'local', + name: 'Test User', + email: 'test@example.com', + password: 'test' + }, { + provider: 'local', + role: 'admin', + name: 'Admin', + email: 'admin@example.com', + password: 'admin' + <% if(filters.mongooseModels) { %>})<% } + if(filters.sequelizeModels) { %>}])<% } %> + .then(() => console.log('finished populating users')) + .catch(err => console.log('error populating users', err)); + }); + promises.push(userPromise);<% } %> - return Promise.all(promises); + return Promise.all(promises); } diff --git a/templates/app/webpack.make.js b/templates/app/webpack.make.js index 86f3df957..2e96494e2 100644 --- a/templates/app/webpack.make.js +++ b/templates/app/webpack.make.js @@ -5,7 +5,6 @@ var webpack = require('webpack'); 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'); @@ -26,6 +25,10 @@ module.exports = function makeWebpackConfig(options) { */ var config = {}; + config.mode = BUILD + ? 'production' + : 'development'; + /** * Entry * Reference: http://webpack.github.io/docs/configuration.html#entry @@ -268,6 +271,12 @@ module.exports = function makeWebpackConfig(options) { * List: http://webpack.github.io/docs/list-of-plugins.html */ config.plugins = [ + // Hides the 'the request of a dependency is an expression' warnings + new webpack.ContextReplacementPlugin( + /angular(\\|\/)core/, + path.resolve(__dirname, '../src') + ), + // Reference: https://github.com/webpack/extract-text-webpack-plugin // Extract css files // Disabled when in test mode or not in build mode @@ -304,16 +313,17 @@ module.exports = function makeWebpackConfig(options) { ]; 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) - })); + // TODO(webpack4) + // 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 app.html in test mode diff --git a/templates/endpoint/basename.controller.js b/templates/endpoint/basename.controller.js index 85bf549ac..e276ef47a 100644 --- a/templates/endpoint/basename.controller.js +++ b/templates/endpoint/basename.controller.js @@ -15,127 +15,125 @@ import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequeli import {<%= classedName %>} from '<%= relativeRequire(config.get('registerModelsFile')) %>';<% } %> function respondWithResult(res, statusCode) { - statusCode = statusCode || 200; - return function(entity) { - if(entity) { - return res.status(statusCode).json(entity); - } - return null; - }; + statusCode = statusCode || 200; + return function(entity) { + if(entity) { + return res.status(statusCode).json(entity); + } + return null; + }; } function patchUpdates(patches) { - return function(entity) { - try { - applyPatch(entity, patches, /*validate*/ true); - } catch(err) { - return Promise.reject(err); - } + return function(entity) { + try { + applyPatch(entity, patches, /*validate*/ true); + } catch(err) { + return Promise.reject(err); + } - return entity.save(); - }; + return entity.save(); + }; } function removeEntity(res) { - return function(entity) { - if(entity) { - <% if(filters.mongooseModels) { %>return entity.remove()<% } - if(filters.sequelizeModels) { %>return entity.destroy()<% } %> - .then(() => res.status(204).end()); - } - }; + return function(entity) { + if(entity) { + <% if(filters.mongooseModels) { %>return entity.remove()<% } + if(filters.sequelizeModels) { %>return entity.destroy()<% } %> + .then(() => res.status(204).end()); + } + }; } function handleEntityNotFound(res) { - return function(entity) { - if(!entity) { - res.status(404).end(); - return null; - } - return entity; - }; + return function(entity) { + if(!entity) { + res.status(404).end(); + return null; + } + return entity; + }; } function handleError(res, statusCode) { - statusCode = statusCode || 500; - return function(err) { - res.status(statusCode).send(err); - }; + statusCode = statusCode || 500; + return function(err) { + res.status(statusCode).send(err); + }; }<% } %> // Gets a list of <%= classedName %>s export function index(req, res) {<% if(!filters.models) { %> - res.json([]);<% } else { %> - <% if(filters.mongooseModels) { %>return <%= classedName %>.find().exec()<% } - if(filters.sequelizeModels) { %>return <%= classedName %>.findAll()<% } %> - .then(respondWithResult(res)) - .catch(handleError(res));<% } %> + res.json([]);<% } else { %> + <% if(filters.mongooseModels) { %>return <%= classedName %>.find().exec()<% } + if(filters.sequelizeModels) { %>return <%= classedName %>.findAll()<% } %> + .then(respondWithResult(res)) + .catch(handleError(res));<% } %> }<% 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({ - where: { - _id: req.params.id - } - })<% } %> - .then(handleEntityNotFound(res)) - .then(respondWithResult(res)) - .catch(handleError(res)); + <% 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(respondWithResult(res)) + .catch(handleError(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)<% } %> - .then(respondWithResult(res, 201)) - .catch(handleError(res)); + <% 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) { - Reflect.deleteProperty(req.body, '_id'); - } - <%_ if(filters.mongooseModels) { -%> - return <%= classedName %>.findOneAndUpdate({_id: req.params.id}, req.body, {new: true, 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)); + if(req.body._id) { + Reflect.deleteProperty(req.body, '_id'); + }<% if(filters.mongooseModels) { %> + return <%= classedName %>.findOneAndUpdate({_id: req.params.id}, req.body, {new: true, 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 patch(req, res) { - if(req.body._id) { - Reflect.deleteProperty(req.body, '_id'); - } - <% if(filters.mongooseModels) { %>return <%= classedName %>.findById(req.params.id).exec()<% } - if(filters.sequelizeModels) { %>return <%= classedName %>.find({ - where: { - _id: req.params.id + if(req.body._id) { + Reflect.deleteProperty(req.body, '_id'); } - })<% } %> - .then(handleEntityNotFound(res)) - .then(patchUpdates(req.body)) - .then(respondWithResult(res)) - .catch(handleError(res)); + <% 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(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({ - where: { - _id: req.params.id - } - })<% } %> - .then(handleEntityNotFound(res)) - .then(removeEntity(res)) - .catch(handleError(res)); + <% 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(removeEntity(res)) + .catch(handleError(res)); }<% } %> diff --git a/templates/endpoint/basename.socket(ws).js b/templates/endpoint/basename.socket(ws).js index 6010c130b..c9fe75274 100644 --- a/templates/endpoint/basename.socket(ws).js +++ b/templates/endpoint/basename.socket(ws).js @@ -10,24 +10,24 @@ import <%= classedName %>Events from './<%= basename %>.events'; var events = ['save', 'remove']; export function register(spark) { - // Bind model events to socket events - for(let event of events) { - var listener = createListener(`<%= cameledName %>:${event}`, spark); + // Bind model events to socket events + for(let event of events) { + var listener = createListener(`<%= cameledName %>:${event}`, spark); - <%= classedName %>Events.on(event, listener); - spark.on('disconnect', removeListener(event, listener)); - } + <%= classedName %>Events.on(event, listener); + spark.on('disconnect', removeListener(event, listener)); + } } function createListener(event, spark) { - return function(doc) { - spark.emit(event, doc); - }; + return function(doc) { + spark.emit(event, doc); + }; } function removeListener(event, listener) { - return function() { - <%= classedName %>Events.removeListener(event, listener); - }; + return function() { + <%= classedName %>Events.removeListener(event, listener); + }; }