diff --git a/.gitignore b/.gitignore
index 2c22538..56a58ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,9 @@
generated/node_modules
generated/bower_components
generated/public
+generated/build
node_modules
bower_components
npm-debug.log
.DS_Store
-coverage/
\ No newline at end of file
+coverage/
diff --git a/generated/.babelrc b/generated/.babelrc
index c13c5f6..1e4e461 100644
--- a/generated/.babelrc
+++ b/generated/.babelrc
@@ -1,3 +1,10 @@
{
- "presets": ["es2015"]
+ "presets": ["es2015", "react", "stage-1", "stage-2"],
+ "env": {
+ "development": {
+ "presets": [
+ "react-hmre"
+ ]
+ }
+ }
}
diff --git a/generated/.eslintrc.json b/generated/.eslintrc.json
index 2158099..99aed32 100644
--- a/generated/.eslintrc.json
+++ b/generated/.eslintrc.json
@@ -2,6 +2,10 @@
{
"root": true,
"extends": "fullstack", // from the `eslint-config-fullstack` npm module,
+ "parser": "babel-eslint",
+ "parserOptions": {
+ "sourceType": "module"
+ },
"env": {
"es6": true,
"node": false,
@@ -13,7 +17,8 @@
"rules": {
"global-require": 0, // 0 = off, 1 = warn (yellow), 2 = error (red)
"camelcase": 0,
- "no-debugger": 1
+ "no-debugger": 1,
+ "no-unused-expressions": 1
// add more rules here to override the fullstack config
}
}
diff --git a/generated/browser/api/auth/index.js b/generated/browser/api/auth/index.js
new file mode 100644
index 0000000..f236e5e
--- /dev/null
+++ b/generated/browser/api/auth/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+import axios from 'axios';
+import { parseJSON, parseData } from '../../utils';
+
+const BASE_URL = process.env.URL || `http://localhost:${process.env.PORT || 1337}`;
+
+export const fetchSession = () => {
+ return axios.get(`${BASE_URL}/session`).then(parseData);
+}
+
+export const tryLogin = (credentials) => {
+ return axios.post(`${BASE_URL}/login`, credentials);
+}
+
+export const tryLogout = () => {
+ return axios.get(`${BASE_URL}/logout`);
+}
+
+const auth = {
+ fetchSession,
+ tryLogin,
+ tryLogout
+}
+
+export default auth;
diff --git a/generated/browser/api/index.js b/generated/browser/api/index.js
new file mode 100644
index 0000000..c6e4055
--- /dev/null
+++ b/generated/browser/api/index.js
@@ -0,0 +1 @@
+export auth from './auth';
diff --git a/generated/browser/api/secretStash/index.js b/generated/browser/api/secretStash/index.js
new file mode 100644
index 0000000..9a6f200
--- /dev/null
+++ b/generated/browser/api/secretStash/index.js
@@ -0,0 +1,16 @@
+'use strict';
+
+import axios from 'axios';
+import { parseData } from '../../utils';
+
+const BASE_URL = process.env.URL || `http://localhost:${process.env.PORT || 1337}`;
+
+export const getStash = () => {
+ return axios.get(`${BASE_URL}/api/members/secret-stash`).then(parseData);
+}
+
+const secretStash = {
+ getStash
+}
+
+export default secretStash;
diff --git a/generated/browser/app.js b/generated/browser/app.js
new file mode 100644
index 0000000..30f3bd6
--- /dev/null
+++ b/generated/browser/app.js
@@ -0,0 +1,23 @@
+'use strict';
+
+import React from 'react';
+import { render } from 'react-dom';
+import { Provider } from 'react-redux';
+import { Router, browserHistory } from 'react-router';
+import { syncHistoryWithStore } from 'react-router-redux';
+import getRoutes from './config/routes';
+import configureStore from './redux/configureStore';
+
+const store = configureStore(browserHistory);
+const history = syncHistoryWithStore(browserHistory, store);
+
+const component = (
+
+);
+
+render(
+
+ {component}
+ ,
+ document.getElementById('app')
+);
diff --git a/generated/browser/components/About/About.js b/generated/browser/components/About/About.js
new file mode 100644
index 0000000..bad8362
--- /dev/null
+++ b/generated/browser/components/About/About.js
@@ -0,0 +1,30 @@
+'use strict';
+
+import React from 'react';
+import { Row, Col, Carousel } from 'react-bootstrap';
+import FullstackPics from '../../resources/FullstackPics';
+import './_About';
+
+const About = (props) => {
+ return (
+
+
+ This website--the very one at which you currently gaze--was created by a basic scaffolding
+ concocted at Fullstack Academy. Here are some of the people who make it all very real:
+
+
+ {
+ FullstackPics.map((pic, i) => {
+ return (
+
+
+
+ )
+ })
+ }
+
+
+ )
+}
+
+export default About;
diff --git a/generated/browser/scss/about/main.scss b/generated/browser/components/About/_About.scss
similarity index 52%
rename from generated/browser/scss/about/main.scss
rename to generated/browser/components/About/_About.scss
index 8e21a04..f3741c7 100644
--- a/generated/browser/scss/about/main.scss
+++ b/generated/browser/components/About/_About.scss
@@ -1,8 +1,6 @@
#about {
- margin: 0 auto;
- width: 90%;
- @include clearfix;
+ text-align: center;
p {
width: 40%;
@@ -18,21 +16,20 @@
width: 55%;
overflow: hidden;
margin: 0 auto;
+
.carousel-inner {
height: 500px !important;
- }
- .carousel-control {
- background: none;
- }
- .carousel-indicators {
- display: none;
- }
- img {
- max-width: 100%;
- max-height: 500px;
- display: block;
- margin: 0 auto;
+
+ img {
+ max-width: 100%;
+ max-height: 500px;
+ display: block;
+ margin: 0 auto;
+ }
}
}
+ .carousel-control.left, .carousel-control.right {
+ background-image: none;
+ }
}
diff --git a/generated/browser/components/About/index.js b/generated/browser/components/About/index.js
new file mode 100644
index 0000000..a1afdc8
--- /dev/null
+++ b/generated/browser/components/About/index.js
@@ -0,0 +1 @@
+export default from './About';
diff --git a/generated/browser/components/Docs/Docs.js b/generated/browser/components/Docs/Docs.js
new file mode 100644
index 0000000..cc1116b
--- /dev/null
+++ b/generated/browser/components/Docs/Docs.js
@@ -0,0 +1,14 @@
+'use strict';
+
+import React from 'react';
+import './_Docs';
+
+const Docs = (props) => {
+ return (
+
+ )
+}
+
+export default Docs;
diff --git a/generated/browser/components/Docs/_Docs.scss b/generated/browser/components/Docs/_Docs.scss
new file mode 100644
index 0000000..d6a2307
--- /dev/null
+++ b/generated/browser/components/Docs/_Docs.scss
@@ -0,0 +1,5 @@
+#docs {
+
+ text-align: center;
+
+}
diff --git a/generated/browser/components/Docs/index.js b/generated/browser/components/Docs/index.js
new file mode 100644
index 0000000..1b243fa
--- /dev/null
+++ b/generated/browser/components/Docs/index.js
@@ -0,0 +1 @@
+export default from './Docs';
diff --git a/generated/browser/components/Home/Home.js b/generated/browser/components/Home/Home.js
new file mode 100644
index 0000000..b68e153
--- /dev/null
+++ b/generated/browser/components/Home/Home.js
@@ -0,0 +1,17 @@
+'use strict';
+
+import React from 'react';
+import Logo from '../../shared/Logo';
+import getRandomGreeting from '../../resources/RandomGreetings';
+import './_Home';
+
+const Home = (props) => {
+ return (
+
+
+
{getRandomGreeting()}
+
+ )
+}
+
+export default Home;
diff --git a/generated/browser/components/Home/_Home.scss b/generated/browser/components/Home/_Home.scss
new file mode 100644
index 0000000..067de55
--- /dev/null
+++ b/generated/browser/components/Home/_Home.scss
@@ -0,0 +1,9 @@
+#home {
+
+ text-align: center;
+
+ .logo {
+ width: 150px;
+ margin: 0 auto;
+ }
+}
diff --git a/generated/browser/components/Home/index.js b/generated/browser/components/Home/index.js
new file mode 100644
index 0000000..f6e9bae
--- /dev/null
+++ b/generated/browser/components/Home/index.js
@@ -0,0 +1 @@
+export default from './Home';
diff --git a/generated/browser/components/MembersOnly/MembersOnly.js b/generated/browser/components/MembersOnly/MembersOnly.js
new file mode 100644
index 0000000..b7488db
--- /dev/null
+++ b/generated/browser/components/MembersOnly/MembersOnly.js
@@ -0,0 +1,39 @@
+'use strict';
+
+import React, { Component } from 'react';
+import { getStash } from '../../api/secretStash';
+import './_MembersOnly';
+
+class MembersOnly extends Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ stash: []
+ };
+
+ this.renderSecretStash = this.renderSecretStash.bind(this);
+ }
+
+ componentWillMount () {
+ getStash()
+ .then(stash => {
+ this.setState({ ...this.state, stash })
+ })
+ }
+
+ renderSecretStash () {
+ return this.state.stash.map(item => ( ));
+ }
+
+ render () {
+ return (
+
+
Members Only Area
+ {this.renderSecretStash()}
+
+ )
+ }
+}
+
+export default MembersOnly;
diff --git a/generated/browser/components/MembersOnly/_MembersOnly.scss b/generated/browser/components/MembersOnly/_MembersOnly.scss
new file mode 100644
index 0000000..0472d54
--- /dev/null
+++ b/generated/browser/components/MembersOnly/_MembersOnly.scss
@@ -0,0 +1,3 @@
+#members-only {
+ text-align: center;
+}
diff --git a/generated/browser/components/MembersOnly/index.js b/generated/browser/components/MembersOnly/index.js
new file mode 100644
index 0000000..1cf7700
--- /dev/null
+++ b/generated/browser/components/MembersOnly/index.js
@@ -0,0 +1 @@
+export default from './MembersOnly';
diff --git a/generated/browser/components/Navbar/Navbar.js b/generated/browser/components/Navbar/Navbar.js
new file mode 100644
index 0000000..c052df3
--- /dev/null
+++ b/generated/browser/components/Navbar/Navbar.js
@@ -0,0 +1,94 @@
+'use strict';
+
+import React, { PropTypes } from 'react';
+import { IndexLink, Link } from 'react-router';
+import { push } from 'react-router-redux';
+import { Navbar, Nav, NavItem, Button } from 'react-bootstrap';
+import { LinkContainer, IndexLinkContainer } from 'react-router-bootstrap';
+import { logout } from '../../redux/modules/auth';
+import Logo from '../../shared/Logo';
+import './_Navbar';
+
+const NAV_ITEMS = [
+ { label: 'Home', path: '/' },
+ { label: 'About', path: 'about' },
+ { label: 'Documentation', path: 'docs' },
+ { label: 'Members Only', path: 'membersOnly', auth: true }
+]
+
+const renderNavItems = (user) => {
+ return (
+
+ {
+ NAV_ITEMS.map((item, i) => {
+ if (item.label === 'Home') {
+ return (
+
+
+ {item.label}
+
+
+ )
+ } else if (user || !item.auth) {
+ return (
+
+
+ {item.label}
+
+
+ )
+ }
+ })
+ }
+
+ )
+}
+
+const renderAuthNavItems = (user, dispatch) => {
+
+ function handleLogout () {
+ dispatch(logout()).then(() => dispatch(push('/login')))
+ }
+
+ if (user) {
+ return (
+
+
+ { user.email }
+
+
+ Logout
+
+
+ )
+ } else {
+ return (
+
+
+ Login
+
+
+ )
+ }
+}
+
+const NavBar = ({ user, dispatch }) => {
+ return (
+
+
+
+
+
+
+ {renderNavItems(user)}
+ {renderAuthNavItems(user, dispatch)}
+
+ )
+}
+
+NavBar.propTypes = {
+ user: PropTypes.object,
+ dispatch: PropTypes.func
+}
+
+export default NavBar;
diff --git a/generated/browser/components/Navbar/_Navbar.scss b/generated/browser/components/Navbar/_Navbar.scss
new file mode 100644
index 0000000..86d76b2
--- /dev/null
+++ b/generated/browser/components/Navbar/_Navbar.scss
@@ -0,0 +1,32 @@
+$red: #ed1b24;
+$inverse: #222;
+$default: #efefef;
+
+.navbar {
+ background-color: white;
+
+ .logo {
+ margin-top: 10px;
+ width: 30px;
+ }
+
+ .navbar-nav>.active>a,
+ .navbar-nav>li>a {
+ padding: 0;
+ margin: 15px;
+ }
+
+ .navbar-nav>.active>a,
+ .navbar-nav>.active>a:focus,
+ .navbar-nav>.active>a:hover,
+ .navbar-nav>li>a:focus,
+ .navbar-nav>li>a:hover {
+ color: $red;
+ background-color: transparent;
+ }
+
+ .navbar-nav>.active>a {
+ background-color: transparent;
+ }
+
+}
diff --git a/generated/browser/components/Navbar/index.js b/generated/browser/components/Navbar/index.js
new file mode 100644
index 0000000..802fb9f
--- /dev/null
+++ b/generated/browser/components/Navbar/index.js
@@ -0,0 +1 @@
+export default from './Navbar';
diff --git a/generated/browser/components/index.js b/generated/browser/components/index.js
new file mode 100644
index 0000000..fb53ec1
--- /dev/null
+++ b/generated/browser/components/index.js
@@ -0,0 +1,5 @@
+export About from './About';
+export Docs from './Docs';
+export Home from './Home';
+export MembersOnly from './MembersOnly';
+
diff --git a/generated/browser/config/routes.js b/generated/browser/config/routes.js
new file mode 100644
index 0000000..ea231d5
--- /dev/null
+++ b/generated/browser/config/routes.js
@@ -0,0 +1,81 @@
+'use strict';
+
+import React from 'react';
+import { Route, IndexRoute } from 'react-router';
+import {
+ isLoaded as isAuthLoaded,
+ load as loadAuth
+} from '../redux/modules/auth';
+import { About, Docs, Home, MembersOnly } from '../components';
+import { Layout, Login } from '../containers';
+import { NotFound } from '../shared';
+
+const getRoutes = (store) => {
+ const getAuth = (nextState, replace, next) => {
+ if (!isAuthLoaded(store.getState())) {
+ store.dispatch(loadAuth())
+ .then(() => next());
+ } else {
+ next();
+ }
+ }
+
+ const requireLogin = (nextState, replace, next) => {
+ function checkAuth () {
+ const { auth: { user } } = store.getState();
+ if (!user) {
+ replace('/');
+ }
+ next();
+ }
+
+ if (!isAuthLoaded(store.getState())) {
+ store.dispatch(loadAuth()).then(checkAuth);
+ } else {
+ checkAuth();
+ }
+ }
+
+ const requireNoUser = (nextState, replace, next) => {
+ function checkAuth () {
+ const { auth: { user } } = store.getState();
+ if (user) {
+ replace('/membersOnly');
+ }
+ next();
+ }
+
+ if (!isAuthLoaded(store.getState())) {
+ store.dispatch(loadAuth()).then(checkAuth)
+ } else {
+ checkAuth();
+ }
+ }
+
+ return (
+
+
+ { /* Home route */ }
+
+
+ { /* Authenticated Routes */ }
+
+
+
+
+ { /* Unauthenticated Routes Only */ }
+
+
+
+
+ { /* Routes */ }
+
+
+
+ { /* Catch all routes */ }
+
+
+ )
+}
+
+export default getRoutes;
diff --git a/generated/browser/containers/Layout/Layout.js b/generated/browser/containers/Layout/Layout.js
new file mode 100644
index 0000000..2614c19
--- /dev/null
+++ b/generated/browser/containers/Layout/Layout.js
@@ -0,0 +1,32 @@
+'use strict';
+
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import Navbar from '../../components/Navbar';
+import './_Layout';
+
+class Layout extends Component {
+
+ static propTypes = {
+ children: PropTypes.object,
+ user: PropTypes.object,
+ dispatch: PropTypes.func
+ }
+
+ render () {
+ const { children, user, dispatch } = this.props
+
+ return (
+
+ )
+ }
+}
+
+const mapStateToProps = (state) => ({ user: state.auth.user });
+
+export default connect(mapStateToProps)(Layout);
diff --git a/generated/browser/containers/Layout/_Layout.scss b/generated/browser/containers/Layout/_Layout.scss
new file mode 100644
index 0000000..b939552
--- /dev/null
+++ b/generated/browser/containers/Layout/_Layout.scss
@@ -0,0 +1,9 @@
+body {
+ font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+
+#main {
+ padding-top: 60px;
+}
+
+@import url(https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700);
diff --git a/generated/browser/containers/Layout/index.js b/generated/browser/containers/Layout/index.js
new file mode 100644
index 0000000..0720a9d
--- /dev/null
+++ b/generated/browser/containers/Layout/index.js
@@ -0,0 +1 @@
+export default from './Layout';
diff --git a/generated/browser/containers/Login/Login.js b/generated/browser/containers/Login/Login.js
new file mode 100644
index 0000000..cb2f1d9
--- /dev/null
+++ b/generated/browser/containers/Login/Login.js
@@ -0,0 +1,30 @@
+'use strict';
+
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { push } from 'react-router-redux';
+import { AuthForm } from '../../shared';
+import { login } from '../../redux/modules/auth';
+
+class Login extends Component {
+
+ static propTypes = {
+ dispatch: PropTypes.func
+ }
+
+ handleLogin = (credentials) => {
+ const { dispatch } = this.props
+ dispatch(login(credentials)).then(() => dispatch(push('/membersOnly')))
+ }
+
+ render () {
+ return (
+
+ )
+ }
+}
+
+export default connect()(Login);
diff --git a/generated/browser/containers/Login/index.js b/generated/browser/containers/Login/index.js
new file mode 100644
index 0000000..5fa85e6
--- /dev/null
+++ b/generated/browser/containers/Login/index.js
@@ -0,0 +1 @@
+export default from './Login';
diff --git a/generated/browser/containers/index.js b/generated/browser/containers/index.js
new file mode 100644
index 0000000..e3c3516
--- /dev/null
+++ b/generated/browser/containers/index.js
@@ -0,0 +1,2 @@
+export Layout from './Layout';
+export Login from './Login';
diff --git a/generated/browser/js/.eslintrc.json b/generated/browser/js/.eslintrc.json
deleted file mode 100644
index 3b74b6a..0000000
--- a/generated/browser/js/.eslintrc.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "env": {
- "browser": true
- },
- "globals": {
- "app": true,
- "_": true
- }
-}
diff --git a/generated/browser/js/about/about.html b/generated/browser/js/about/about.html
deleted file mode 100644
index c8406a7..0000000
--- a/generated/browser/js/about/about.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- This website--the very one at which you currently gaze--was created by a basic scaffolding
- concocted at Fullstack Academy. Here are some of the people who make it all very real:
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/generated/browser/js/about/about.js b/generated/browser/js/about/about.js
deleted file mode 100644
index 67c42d1..0000000
--- a/generated/browser/js/about/about.js
+++ /dev/null
@@ -1,17 +0,0 @@
-app.config(function ($stateProvider) {
-
- // Register our *about* state.
- $stateProvider.state('about', {
- url: '/about',
- controller: 'AboutController',
- templateUrl: 'js/about/about.html'
- });
-
-});
-
-app.controller('AboutController', function ($scope, FullstackPics) {
-
- // Images of beautiful Fullstack people.
- $scope.images = _.shuffle(FullstackPics);
-
-});
diff --git a/generated/browser/js/app.js b/generated/browser/js/app.js
deleted file mode 100644
index a5de5cb..0000000
--- a/generated/browser/js/app.js
+++ /dev/null
@@ -1,63 +0,0 @@
-'use strict';
-window.app = angular.module('FullstackGeneratedApp', ['fsaPreBuilt', 'ui.router', 'ui.bootstrap', 'ngAnimate']);
-
-app.config(function ($urlRouterProvider, $locationProvider) {
- // This turns off hashbang urls (/#about) and changes it to something normal (/about)
- $locationProvider.html5Mode(true);
- // If we go to a URL that ui-router doesn't have registered, go to the "/" url.
- $urlRouterProvider.otherwise('/');
- // Trigger page refresh when accessing an OAuth route
- $urlRouterProvider.when('/auth/:provider', function () {
- window.location.reload();
- });
-});
-
-// This app.run is for listening to errors broadcasted by ui-router, usually originating from resolves
-app.run(function ($rootScope) {
- $rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, thrownError) {
- console.info(`The following error was thrown by ui-router while transitioning to state "${toState.name}". The origin of this error is probably a resolve function:`);
- console.error(thrownError);
- });
-});
-
-// This app.run is for controlling access to specific states.
-app.run(function ($rootScope, AuthService, $state) {
-
- // The given state requires an authenticated user.
- var destinationStateRequiresAuth = function (state) {
- return state.data && state.data.authenticate;
- };
-
- // $stateChangeStart is an event fired
- // whenever the process of changing a state begins.
- $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
-
- if (!destinationStateRequiresAuth(toState)) {
- // The destination state does not require authentication
- // Short circuit with return.
- return;
- }
-
- if (AuthService.isAuthenticated()) {
- // The user is authenticated.
- // Short circuit with return.
- return;
- }
-
- // Cancel navigating to new state.
- event.preventDefault();
-
- AuthService.getLoggedInUser().then(function (user) {
- // If a user is retrieved, then renavigate to the destination
- // (the second time, AuthService.isAuthenticated() will work)
- // otherwise, if no user is logged in, go to "login" state.
- if (user) {
- $state.go(toState.name, toParams);
- } else {
- $state.go('login');
- }
- });
-
- });
-
-});
diff --git a/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.html b/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.html
deleted file mode 100644
index aab0a2a..0000000
--- a/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.js b/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.js
deleted file mode 100644
index b6768eb..0000000
--- a/generated/browser/js/common/directives/fullstack-logo/fullstack-logo.js
+++ /dev/null
@@ -1,6 +0,0 @@
-app.directive('fullstackLogo', function () {
- return {
- restrict: 'E',
- templateUrl: 'js/common/directives/fullstack-logo/fullstack-logo.html'
- };
-});
diff --git a/generated/browser/js/common/directives/navbar/navbar.html b/generated/browser/js/common/directives/navbar/navbar.html
deleted file mode 100644
index 9fdafe9..0000000
--- a/generated/browser/js/common/directives/navbar/navbar.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
Login
-
-
Welcome, {{ user.email }}!
-
Logout
-
-
-
\ No newline at end of file
diff --git a/generated/browser/js/common/directives/navbar/navbar.js b/generated/browser/js/common/directives/navbar/navbar.js
deleted file mode 100644
index b3c8237..0000000
--- a/generated/browser/js/common/directives/navbar/navbar.js
+++ /dev/null
@@ -1,48 +0,0 @@
-app.directive('navbar', function ($rootScope, AuthService, AUTH_EVENTS, $state) {
-
- return {
- restrict: 'E',
- scope: {},
- templateUrl: 'js/common/directives/navbar/navbar.html',
- link: function (scope) {
-
- scope.items = [
- { label: 'Home', state: 'home' },
- { label: 'About', state: 'about' },
- { label: 'Documentation', state: 'docs' },
- { label: 'Members Only', state: 'membersOnly', auth: true }
- ];
-
- scope.user = null;
-
- scope.isLoggedIn = function () {
- return AuthService.isAuthenticated();
- };
-
- scope.logout = function () {
- AuthService.logout().then(function () {
- $state.go('home');
- });
- };
-
- var setUser = function () {
- AuthService.getLoggedInUser().then(function (user) {
- scope.user = user;
- });
- };
-
- var removeUser = function () {
- scope.user = null;
- };
-
- setUser();
-
- $rootScope.$on(AUTH_EVENTS.loginSuccess, setUser);
- $rootScope.$on(AUTH_EVENTS.logoutSuccess, removeUser);
- $rootScope.$on(AUTH_EVENTS.sessionTimeout, removeUser);
-
- }
-
- };
-
-});
diff --git a/generated/browser/js/common/directives/rando-greeting/rando-greeting.html b/generated/browser/js/common/directives/rando-greeting/rando-greeting.html
deleted file mode 100644
index b25b763..0000000
--- a/generated/browser/js/common/directives/rando-greeting/rando-greeting.html
+++ /dev/null
@@ -1 +0,0 @@
-{{ greeting }}
\ No newline at end of file
diff --git a/generated/browser/js/common/directives/rando-greeting/rando-greeting.js b/generated/browser/js/common/directives/rando-greeting/rando-greeting.js
deleted file mode 100644
index ebab94a..0000000
--- a/generated/browser/js/common/directives/rando-greeting/rando-greeting.js
+++ /dev/null
@@ -1,11 +0,0 @@
-app.directive('randoGreeting', function (RandomGreetings) {
-
- return {
- restrict: 'E',
- templateUrl: 'js/common/directives/rando-greeting/rando-greeting.html',
- link: function (scope) {
- scope.greeting = RandomGreetings.getRandomGreeting();
- }
- };
-
-});
diff --git a/generated/browser/js/common/factories/FullstackPics.js b/generated/browser/js/common/factories/FullstackPics.js
deleted file mode 100644
index baa2e28..0000000
--- a/generated/browser/js/common/factories/FullstackPics.js
+++ /dev/null
@@ -1,30 +0,0 @@
-app.factory('FullstackPics', function () {
- return [
- 'https://pbs.twimg.com/media/B7gBXulCAAAXQcE.jpg:large',
- 'https://fbcdn-sphotos-c-a.akamaihd.net/hphotos-ak-xap1/t31.0-8/10862451_10205622990359241_8027168843312841137_o.jpg',
- 'https://pbs.twimg.com/media/B-LKUshIgAEy9SK.jpg',
- 'https://pbs.twimg.com/media/B79-X7oCMAAkw7y.jpg',
- 'https://pbs.twimg.com/media/B-Uj9COIIAIFAh0.jpg:large',
- 'https://pbs.twimg.com/media/B6yIyFiCEAAql12.jpg:large',
- 'https://pbs.twimg.com/media/CE-T75lWAAAmqqJ.jpg:large',
- 'https://pbs.twimg.com/media/CEvZAg-VAAAk932.jpg:large',
- 'https://pbs.twimg.com/media/CEgNMeOXIAIfDhK.jpg:large',
- 'https://pbs.twimg.com/media/CEQyIDNWgAAu60B.jpg:large',
- 'https://pbs.twimg.com/media/CCF3T5QW8AE2lGJ.jpg:large',
- 'https://pbs.twimg.com/media/CAeVw5SWoAAALsj.jpg:large',
- 'https://pbs.twimg.com/media/CAaJIP7UkAAlIGs.jpg:large',
- 'https://pbs.twimg.com/media/CAQOw9lWEAAY9Fl.jpg:large',
- 'https://pbs.twimg.com/media/B-OQbVrCMAANwIM.jpg:large',
- 'https://pbs.twimg.com/media/B9b_erwCYAAwRcJ.png:large',
- 'https://pbs.twimg.com/media/B5PTdvnCcAEAl4x.jpg:large',
- 'https://pbs.twimg.com/media/B4qwC0iCYAAlPGh.jpg:large',
- 'https://pbs.twimg.com/media/B2b33vRIUAA9o1D.jpg:large',
- 'https://pbs.twimg.com/media/BwpIwr1IUAAvO2_.jpg:large',
- 'https://pbs.twimg.com/media/BsSseANCYAEOhLw.jpg:large',
- 'https://pbs.twimg.com/media/CJ4vLfuUwAAda4L.jpg:large',
- 'https://pbs.twimg.com/media/CI7wzjEVEAAOPpS.jpg:large',
- 'https://pbs.twimg.com/media/CIdHvT2UsAAnnHV.jpg:large',
- 'https://pbs.twimg.com/media/CGCiP_YWYAAo75V.jpg:large',
- 'https://pbs.twimg.com/media/CIS4JPIWIAI37qu.jpg:large'
- ];
-});
diff --git a/generated/browser/js/common/factories/RandomGreetings.js b/generated/browser/js/common/factories/RandomGreetings.js
deleted file mode 100644
index 55d7390..0000000
--- a/generated/browser/js/common/factories/RandomGreetings.js
+++ /dev/null
@@ -1,29 +0,0 @@
-app.factory('RandomGreetings', function () {
-
- var getRandomFromArray = function (arr) {
- return arr[Math.floor(Math.random() * arr.length)];
- };
-
- var greetings = [
- 'Hello, world!',
- 'At long last, I live!',
- 'Hello, simple human.',
- 'What a beautiful day!',
- 'I\'m like any other project, except that I am yours. :)',
- 'This empty string is for Lindsay Levine.',
- 'こんにちは、ユーザー様。',
- 'Welcome. To. WEBSITE.',
- ':D',
- 'Yes, I think we\'ve met before.',
- 'Gimme 3 mins... I just grabbed this really dope frittata',
- 'If Cooper could offer only one piece of advice, it would be to nevSQUIRREL!',
- ];
-
- return {
- greetings: greetings,
- getRandomGreeting: function () {
- return getRandomFromArray(greetings);
- }
- };
-
-});
diff --git a/generated/browser/js/docs/docs.html b/generated/browser/js/docs/docs.html
deleted file mode 100644
index 54da1f7..0000000
--- a/generated/browser/js/docs/docs.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/generated/browser/js/docs/docs.js b/generated/browser/js/docs/docs.js
deleted file mode 100644
index 4df733b..0000000
--- a/generated/browser/js/docs/docs.js
+++ /dev/null
@@ -1,6 +0,0 @@
-app.config(function ($stateProvider) {
- $stateProvider.state('docs', {
- url: '/docs',
- templateUrl: 'js/docs/docs.html'
- });
-});
diff --git a/generated/browser/js/fsa/fsa-pre-built.js b/generated/browser/js/fsa/fsa-pre-built.js
deleted file mode 100644
index 6f5bd13..0000000
--- a/generated/browser/js/fsa/fsa-pre-built.js
+++ /dev/null
@@ -1,130 +0,0 @@
-(function () {
-
- 'use strict';
-
- // Hope you didn't forget Angular! Duh-doy.
- if (!window.angular) throw new Error('I can\'t find Angular!');
-
- var app = angular.module('fsaPreBuilt', []);
-
- app.factory('Socket', function () {
- if (!window.io) throw new Error('socket.io not found!');
- return window.io(window.location.origin);
- });
-
- // AUTH_EVENTS is used throughout our app to
- // broadcast and listen from and to the $rootScope
- // for important events about authentication flow.
- app.constant('AUTH_EVENTS', {
- loginSuccess: 'auth-login-success',
- loginFailed: 'auth-login-failed',
- logoutSuccess: 'auth-logout-success',
- sessionTimeout: 'auth-session-timeout',
- notAuthenticated: 'auth-not-authenticated',
- notAuthorized: 'auth-not-authorized'
- });
-
- app.factory('AuthInterceptor', function ($rootScope, $q, AUTH_EVENTS) {
- var statusDict = {
- 401: AUTH_EVENTS.notAuthenticated,
- 403: AUTH_EVENTS.notAuthorized,
- 419: AUTH_EVENTS.sessionTimeout,
- 440: AUTH_EVENTS.sessionTimeout
- };
- return {
- responseError: function (response) {
- $rootScope.$broadcast(statusDict[response.status], response);
- return $q.reject(response)
- }
- };
- });
-
- app.config(function ($httpProvider) {
- $httpProvider.interceptors.push([
- '$injector',
- function ($injector) {
- return $injector.get('AuthInterceptor');
- }
- ]);
- });
-
- app.service('AuthService', function ($http, Session, $rootScope, AUTH_EVENTS, $q) {
-
- function onSuccessfulLogin(response) {
- var user = response.data.user;
- Session.create(user);
- $rootScope.$broadcast(AUTH_EVENTS.loginSuccess);
- return user;
- }
-
- // Uses the session factory to see if an
- // authenticated user is currently registered.
- this.isAuthenticated = function () {
- return !!Session.user;
- };
-
- this.getLoggedInUser = function (fromServer) {
-
- // If an authenticated session exists, we
- // return the user attached to that session
- // with a promise. This ensures that we can
- // always interface with this method asynchronously.
-
- // Optionally, if true is given as the fromServer parameter,
- // then this cached value will not be used.
-
- if (this.isAuthenticated() && fromServer !== true) {
- return $q.when(Session.user);
- }
-
- // Make request GET /session.
- // If it returns a user, call onSuccessfulLogin with the response.
- // If it returns a 401 response, we catch it and instead resolve to null.
- return $http.get('/session').then(onSuccessfulLogin).catch(function () {
- return null;
- });
-
- };
-
- this.login = function (credentials) {
- return $http.post('/login', credentials)
- .then(onSuccessfulLogin)
- .catch(function () {
- return $q.reject({ message: 'Invalid login credentials.' });
- });
- };
-
- this.logout = function () {
- return $http.get('/logout').then(function () {
- Session.destroy();
- $rootScope.$broadcast(AUTH_EVENTS.logoutSuccess);
- });
- };
-
- });
-
- app.service('Session', function ($rootScope, AUTH_EVENTS) {
-
- var self = this;
-
- $rootScope.$on(AUTH_EVENTS.notAuthenticated, function () {
- self.destroy();
- });
-
- $rootScope.$on(AUTH_EVENTS.sessionTimeout, function () {
- self.destroy();
- });
-
- this.user = null;
-
- this.create = function (user) {
- this.user = user;
- };
-
- this.destroy = function () {
- this.user = null;
- };
-
- });
-
-}());
diff --git a/generated/browser/js/home/home.html b/generated/browser/js/home/home.html
deleted file mode 100644
index 6141819..0000000
--- a/generated/browser/js/home/home.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/generated/browser/js/home/home.js b/generated/browser/js/home/home.js
deleted file mode 100644
index 55f55e0..0000000
--- a/generated/browser/js/home/home.js
+++ /dev/null
@@ -1,6 +0,0 @@
-app.config(function ($stateProvider) {
- $stateProvider.state('home', {
- url: '/',
- templateUrl: 'js/home/home.html'
- });
-});
diff --git a/generated/browser/js/login/login.html b/generated/browser/js/login/login.html
deleted file mode 100644
index 4fee030..0000000
--- a/generated/browser/js/login/login.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
\ No newline at end of file
diff --git a/generated/browser/js/login/login.js b/generated/browser/js/login/login.js
deleted file mode 100644
index afb6bcb..0000000
--- a/generated/browser/js/login/login.js
+++ /dev/null
@@ -1,28 +0,0 @@
-app.config(function ($stateProvider) {
-
- $stateProvider.state('login', {
- url: '/login',
- templateUrl: 'js/login/login.html',
- controller: 'LoginCtrl'
- });
-
-});
-
-app.controller('LoginCtrl', function ($scope, AuthService, $state) {
-
- $scope.login = {};
- $scope.error = null;
-
- $scope.sendLogin = function (loginInfo) {
-
- $scope.error = null;
-
- AuthService.login(loginInfo).then(function () {
- $state.go('home');
- }).catch(function () {
- $scope.error = 'Invalid login credentials.';
- });
-
- };
-
-});
diff --git a/generated/browser/js/members-only/members-only.js b/generated/browser/js/members-only/members-only.js
deleted file mode 100644
index d95bdc1..0000000
--- a/generated/browser/js/members-only/members-only.js
+++ /dev/null
@@ -1,32 +0,0 @@
-app.config(function ($stateProvider) {
-
- $stateProvider.state('membersOnly', {
- url: '/members-area',
- template: ' ',
- controller: function ($scope, SecretStash) {
- SecretStash.getStash().then(function (stash) {
- $scope.stash = stash;
- });
- },
- // The following data.authenticate is read by an event listener
- // that controls access to this state. Refer to app.js.
- data: {
- authenticate: true
- }
- });
-
-});
-
-app.factory('SecretStash', function ($http) {
-
- var getStash = function () {
- return $http.get('/api/members/secret-stash').then(function (response) {
- return response.data;
- });
- };
-
- return {
- getStash: getStash
- };
-
-});
diff --git a/generated/browser/redux/configureStore.js b/generated/browser/redux/configureStore.js
new file mode 100644
index 0000000..85b2745
--- /dev/null
+++ b/generated/browser/redux/configureStore.js
@@ -0,0 +1,28 @@
+'use strict';
+
+import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
+import { routerMiddleware, routerReducer as routing } from 'react-router-redux'
+import createLogger from 'redux-logger'
+import thunk from 'redux-thunk'
+import promise from './middleware/promise'
+import auth from './modules/auth'
+
+export default function configureStore (history, initialState) {
+
+ const reducer = combineReducers({
+ auth,
+ routing
+ })
+
+ const middleware = [thunk, promise, routerMiddleware(history)]
+ if (process.env.NODE_ENV !== 'production') {
+ middleware.push(createLogger())
+ }
+
+ const enhancers = compose(
+ applyMiddleware(...middleware),
+ window.devToolsExtension ? window.devToolsExtension() : f => f
+ )
+
+ return createStore(reducer, initialState, enhancers)
+}
diff --git a/generated/browser/redux/middleware/promise.js b/generated/browser/redux/middleware/promise.js
new file mode 100644
index 0000000..97b673e
--- /dev/null
+++ b/generated/browser/redux/middleware/promise.js
@@ -0,0 +1,29 @@
+'use strict';
+
+import { parseJSON, parseData } from '../../utils'
+
+export default () => next => action => {
+
+ const { promise, types, ...rest } = action
+
+ if (!promise) {
+ return next(action)
+ }
+
+ const [REQUEST, SUCCESS, FAILURE] = types
+ next({ ...rest, type: REQUEST })
+
+ return promise
+ .then(parseData)
+ .then(
+ result => next({ ...rest, result, type: SUCCESS }),
+ error => next({ ...rest, error, type: FAILURE})
+ )
+ .catch(error => {
+ next({
+ ...rest,
+ error: `MIDDLEWARE ERROR: ${error.message}`,
+ type: FAILURE
+ })
+ })
+}
diff --git a/generated/browser/redux/modules/auth/auth.test.js b/generated/browser/redux/modules/auth/auth.test.js
new file mode 100644
index 0000000..4d5fdfa
--- /dev/null
+++ b/generated/browser/redux/modules/auth/auth.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+import { expect } from 'chai';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import promise from '../../middleware/promise';
+import reducer from '../auth';
+import * as test from './tests';
+
+const middlewares = [ thunk, promise ];
+const mockStore = configureMockStore(middlewares);
+
+describe('MODULE - auth:', () => {
+ describe('ACTIONS', () => {
+ let store = mockStore({ session: {} });
+
+ // isLoaded action test
+ test.isLoaded(mockStore);
+
+ // load action tests
+ test.load(mockStore);
+
+ // login action tests
+ test.login(mockStore);
+
+ // logout action tests
+ test.logout(mockStore);
+
+ });
+
+ describe('REDUCER', () => {
+
+ const initialState = {
+ loaded: false
+ };
+
+ it('returns the initial state', () => {
+ expect(reducer(undefined, {})).to.deep.equal(initialState)
+ });
+
+ describe('handles actions', () => {
+ // LOAD action tests
+ test.loadActions();
+
+ // LOGIN action tests
+ test.loginActions();
+
+ // LOGOUT action tests
+ test.logoutActions();
+ });
+
+ });
+
+});
diff --git a/generated/browser/redux/modules/auth/index.js b/generated/browser/redux/modules/auth/index.js
new file mode 100644
index 0000000..7931383
--- /dev/null
+++ b/generated/browser/redux/modules/auth/index.js
@@ -0,0 +1,105 @@
+'use strict';
+
+import { push } from 'react-router-redux';
+import { auth } from '../../../api';
+
+export const LOAD = 'LOAD';
+export const LOAD_SUCCESS = 'LOAD_SUCCESS';
+export const LOAD_FAILURE = 'LOAD_FAILURE';
+export const LOGIN = 'LOGIN';
+export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
+export const LOGIN_FAILURE = 'LOGIN_FAILURE';
+export const LOGOUT = 'LOGOUT';
+export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
+export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
+
+export function isLoaded (globalState) {
+ return globalState.auth && globalState.auth.loaded
+}
+
+export const load = () => (dispatch, getState) => {
+ dispatch({ type: LOAD });
+ const { auth: { user } } = getState();
+
+ if (user) {
+ dispatch({ type: LOAD_SUCCESS, user });
+ } else {
+ return auth.fetchSession()
+ .then(
+ result => dispatch({ type: LOAD_SUCCESS, result }),
+ error => dispatch({ type: LOAD_FAILURE, error })
+ )
+ .catch(error => {
+ dispatch({
+ type: LOAD_FAILURE,
+ error: error.message || `An error occured`
+ })
+ });
+ }
+}
+
+export function login (credentials) {
+ return {
+ types: [LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE],
+ promise: auth.tryLogin(credentials)
+ }
+}
+
+export function logout () {
+ return {
+ types: [LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAILURE],
+ promise: auth.tryLogout()
+ }
+}
+
+const initialState = {
+ loaded: false
+}
+
+export default function reducer (state = initialState, action) {
+ switch (action.type) {
+ case LOAD:
+ return {
+ ...state,
+ loading: true,
+ loadError: null
+ }
+ case LOGIN:
+ case LOGOUT:
+ return {
+ ...state,
+ loading: true,
+ error: null
+ }
+ case LOAD_SUCCESS:
+ case LOGIN_SUCCESS:
+ return {
+ ...state,
+ loading: false,
+ loaded: true,
+ user: action.result.user
+ }
+ case LOGOUT_SUCCESS:
+ return {
+ ...state,
+ loading: false,
+ loaded: false,
+ user: null
+ }
+ case LOAD_FAILURE:
+ return {
+ ...state,
+ loading: false,
+ loadError: action.error.response
+ }
+ case LOGIN_FAILURE:
+ case LOGOUT_FAILURE:
+ return {
+ ...state,
+ loading: false,
+ error: action.error.response
+ }
+ default:
+ return state
+ }
+}
diff --git a/generated/browser/redux/modules/auth/tests/index.js b/generated/browser/redux/modules/auth/tests/index.js
new file mode 100644
index 0000000..d0f40e5
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/index.js
@@ -0,0 +1,7 @@
+export isLoaded from './isLoaded.test';
+export load from './load.test';
+export loadActions from './loadActions.test';
+export login from './login.test';
+export loginActions from './loginActions.test';
+export logout from './logout.test';
+export logoutActions from './logoutActions.test';
diff --git a/generated/browser/redux/modules/auth/tests/isLoaded.test.js b/generated/browser/redux/modules/auth/tests/isLoaded.test.js
new file mode 100644
index 0000000..633349b
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/isLoaded.test.js
@@ -0,0 +1,20 @@
+'use strict';
+
+import { expect } from 'chai';
+import { isLoaded } from '../../auth';
+
+export default function (mockStore) {
+ describe('isLoaded', () => {
+ let store;
+
+ it('returns true if session has been loaded', () => {
+ store = mockStore({ auth: { loaded: true }});
+ expect(isLoaded(store.getState())).to.be.true;
+ });
+
+ it('returns false if session has not been loaded', () => {
+ store = mockStore({ auth: { loaded: false }});
+ expect(isLoaded(store.getState())).to.be.false;
+ });
+ });
+}
diff --git a/generated/browser/redux/modules/auth/tests/load.test.js b/generated/browser/redux/modules/auth/tests/load.test.js
new file mode 100644
index 0000000..1c727a3
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/load.test.js
@@ -0,0 +1,64 @@
+'use strict';
+
+import { expect } from 'chai';
+import nock from 'nock';
+import {
+ load,
+ LOAD, LOAD_SUCCESS, LOAD_FAILURE
+} from '../../auth';
+
+const credentials = {
+ email: 'test@test.com',
+ password: 'test1234'
+};
+
+export default function (mockStore) {
+ describe('load', () => {
+ let store;
+
+ beforeEach(() => {
+ store = mockStore({ auth: {} });
+ });
+
+ afterEach(() => {
+ nock.cleanAll();
+ });
+
+ const sessionAPICall = nock(`http://localhost:${process.env.PORT || 1337}`)
+ .get('/session');
+
+ it('creates LOAD when initially dispatched', () => {
+ sessionAPICall.reply(200, credentials);
+
+ return store.dispatch(load())
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type)
+ expect(actionTypes).to.include(LOAD)
+ });
+ });
+
+ it('creates LOAD_SUCCESS with result when load was successfully done', () => {
+ sessionAPICall.reply(200, credentials);
+
+ return store.dispatch(load())
+ .then(() => {
+ const actions = store.getActions().filter(action => {
+ return action.result && action.type === LOAD_SUCCESS
+ });
+ expect(actions).to.have.length(1);
+ expect(actions[0].type).to.equal(LOAD_SUCCESS);
+ expect(actions[0].result).to.deep.equal(credentials);
+ });
+ });
+
+ it ('creates LOAD_FAILURE when load request fails', () => {
+ sessionAPICall.replyWithError({ message: 'Error occured' });
+
+ return store.dispatch(load())
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type);
+ expect(actionTypes).to.include(LOAD_FAILURE);
+ });
+ });
+ });
+}
diff --git a/generated/browser/redux/modules/auth/tests/loadActions.test.js b/generated/browser/redux/modules/auth/tests/loadActions.test.js
new file mode 100644
index 0000000..30e1bfd
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/loadActions.test.js
@@ -0,0 +1,71 @@
+'use strict';
+
+import { expect } from 'chai';
+import reducer, { LOAD, LOAD_SUCCESS, LOAD_FAILURE } from '../../auth';
+
+const successRes = {
+ user: { email: 'test@test.com', password: 'test1234' }
+};
+
+const failureRes = {
+ response: { data: 'ERROR' }
+};
+
+export default function () {
+ describe('LOAD', () => {
+ const nextState = reducer(
+ { loading: false, loadError: failureRes },
+ { type: LOAD }
+ );
+
+ it('by setting \'loading\' to true', () => {
+ expect(nextState.loading).to.be.true;
+ });
+
+ it('by setting \'loadError\' to null', () => {
+ expect(nextState.loadError).to.be.null;
+ });
+ });
+
+ describe('LOAD_SUCCESS', () => {
+ const nextState = reducer(
+ { loading: true, loaded: false },
+ { type: LOAD_SUCCESS, result: successRes }
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'loaded\' to true', () => {
+ expect(nextState.loaded).to.be.true;
+ });
+
+ it('by setting \'user\'', () => {
+ expect(nextState.user).to.exist;
+ });
+ });
+
+ describe('LOAD_FAILURE', () => {
+ const nextState = reducer(
+ { loading: true, loaded: false },
+ { type: LOAD_FAILURE, error: failureRes}
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'loadError\'', () => {
+ expect(nextState.loadError).to.exist;
+ });
+
+ it('by not setting \'loaded\' to true', () => {
+ expect(nextState.loaded).to.be.false;
+ });
+
+ it('by not setting \'user\'', () => {
+ expect(nextState.user).to.not.exist;
+ });
+ });
+}
diff --git a/generated/browser/redux/modules/auth/tests/login.test.js b/generated/browser/redux/modules/auth/tests/login.test.js
new file mode 100644
index 0000000..f095ed9
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/login.test.js
@@ -0,0 +1,66 @@
+'use strict';
+
+import { expect } from 'chai';
+import nock from 'nock';
+import {
+ login,
+ LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE
+} from '../../auth';
+
+const credentials = {
+ email: 'test@test.com',
+ password: 'test1234'
+};
+
+export default function (mockStore) {
+ describe('login', () => {
+ let store;
+
+ beforeEach(() => {
+ store = mockStore({ auth: {} });
+ })
+
+ afterEach(() => {
+ nock.cleanAll();
+ })
+
+ const loginAPICall = () => nock(`http://localhost:${process.env.PORT || 1337}`)
+ .post('/login', credentials);
+
+ it('creates LOGIN when initially dispatched', () => {
+ loginAPICall().reply(200, credentials);
+
+ return store.dispatch(login(credentials))
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type)
+ expect(actionTypes).to.include(LOGIN)
+ });
+ })
+
+ it('creates LOGIN_SUCCESS when login was successfully done', () => {
+ loginAPICall().reply(200, credentials);
+
+ return store.dispatch(login(credentials))
+ .then(() => {
+ const actions = store.getActions().filter(action => {
+ return action.result && action.type === LOGIN_SUCCESS
+ });
+ expect(actions).to.have.length(1);
+ expect(actions[0].type).to.equal(LOGIN_SUCCESS);
+ expect(actions[0].result).to.deep.equal(credentials);
+ });
+ })
+
+ it('creates LOGIN_FAILURE when login request fails', () => {
+ loginAPICall().replyWithError({ message: 'Invalid credentials' });
+
+ return store.dispatch(login(credentials))
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type);
+ expect(actionTypes).to.include(LOGIN_FAILURE);
+ });
+ });
+ });
+}
+
+
diff --git a/generated/browser/redux/modules/auth/tests/loginActions.test.js b/generated/browser/redux/modules/auth/tests/loginActions.test.js
new file mode 100644
index 0000000..86cb76e
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/loginActions.test.js
@@ -0,0 +1,71 @@
+'use strict';
+
+import { expect } from 'chai';
+import reducer, { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../../auth';
+
+const successRes = {
+ user: { email: 'test@test.com', password: 'test1234' }
+};
+
+const failureRes = {
+ response: { data: 'ERROR' }
+};
+
+export default function () {
+ describe('LOGIN', () => {
+ const nextState = reducer(
+ { loading: false, error: failureRes },
+ { type: LOGIN }
+ );
+
+ it('by setting \'loading\' to true', () => {
+ expect(nextState.loading).to.be.true;
+ });
+
+ it('by setting \'error\' to null', () => {
+ expect(nextState.error).to.be.null;
+ });
+ });
+
+ describe('LOGIN_SUCCESS', () => {
+ const nextState = reducer(
+ { loading: true, loaded: false },
+ { type: LOGIN_SUCCESS, result: successRes }
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'loaded\' to true', () => {
+ expect(nextState.loaded).to.be.true;
+ });
+
+ it('by setting \'user\'', () => {
+ expect(nextState.user).to.exist;
+ });
+ });
+
+ describe('LOGIN_FAILURE', () => {
+ const nextState = reducer(
+ { loading: true, loaded: false },
+ { type: LOGIN_FAILURE, error: failureRes}
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'error\'', () => {
+ expect(nextState.error).to.exist;
+ });
+
+ it('by not setting \'loaded\' to true', () => {
+ expect(nextState.loaded).to.be.false;
+ });
+
+ it('by not setting \'user\'', () => {
+ expect(nextState.user).to.not.exist;
+ });
+ });
+}
diff --git a/generated/browser/redux/modules/auth/tests/logout.test.js b/generated/browser/redux/modules/auth/tests/logout.test.js
new file mode 100644
index 0000000..8acead3
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/logout.test.js
@@ -0,0 +1,60 @@
+'use strict';
+
+import { expect } from 'chai';
+import nock from 'nock';
+import {
+ logout,
+ LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAILURE
+} from '../../auth';
+
+const credentials = {
+ email: 'test@test.com',
+ password: 'test1234'
+};
+
+export default function (mockStore) {
+ describe('logout', () => {
+ let store;
+
+ beforeEach(() => {
+ store = mockStore({ auth: {} });
+ });
+
+ afterEach(() => {
+ nock.cleanAll();
+ });
+
+ const logoutAPICall = nock(`http://localhost:${process.env.PORT || 1337}`)
+ .get('/logout');
+
+ it('creates LOGOUT when initially dispatched', () => {
+ logoutAPICall.reply(200);
+
+ return store.dispatch(logout())
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type);
+ expect(actionTypes).to.include(LOGOUT);
+ });
+ });
+
+ it('creates LOGOUT_SUCCESS when login was successfully done', () => {
+ logoutAPICall.reply(200);
+
+ return store.dispatch(logout())
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type);
+ expect(actionTypes).to.include(LOGOUT_SUCCESS);
+ });
+ });
+
+ it('creates LOGOUT_FAILURE when login request fails', () => {
+ logoutAPICall.replyWithError({ message: 'Logout Failed' });
+
+ return store.dispatch(logout())
+ .then(() => {
+ const actionTypes = store.getActions().map(action => action.type);
+ expect(actionTypes).to.include(LOGOUT_FAILURE);
+ });
+ });
+ });
+}
diff --git a/generated/browser/redux/modules/auth/tests/logoutActions.test.js b/generated/browser/redux/modules/auth/tests/logoutActions.test.js
new file mode 100644
index 0000000..0f8f4ba
--- /dev/null
+++ b/generated/browser/redux/modules/auth/tests/logoutActions.test.js
@@ -0,0 +1,72 @@
+'use strict';
+
+import { expect } from 'chai';
+import reducer, { LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAILURE } from '../../auth';
+
+const credentials = {
+ email: 'test@test.com',
+ password: 'test1234'
+};
+
+const failureRes = {
+ response: { data: 'ERROR' }
+};
+
+export default function () {
+ describe('LOGOUT', () => {
+ const nextState = reducer(
+ { loading: false, error: failureRes, user: credentials },
+ { type: LOGOUT }
+ );
+
+ it('by setting \'loading\' to true', () => {
+ expect(nextState.loading).to.be.true;
+ });
+
+ it('by setting \'error\' to null', () => {
+ expect(nextState.error).to.be.null;
+ });
+ });
+
+ describe('LOGOUT_SUCCESS', () => {
+ const nextState = reducer(
+ { loading: true, loaded: true, user: credentials },
+ { type: LOGOUT_SUCCESS }
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'loaded\' to false', () => {
+ expect(nextState.loaded).to.be.false;
+ });
+
+ it('by setting \'user\' to null', () => {
+ expect(nextState.user).to.be.null;
+ });
+ });
+
+ describe('LOGOUT_FAILURE', () => {
+ const nextState = reducer(
+ { loading: true, loaded: true, user: credentials },
+ { type: LOGOUT_FAILURE, error: failureRes }
+ );
+
+ it('by setting \'loading\' to false', () => {
+ expect(nextState.loading).to.be.false;
+ });
+
+ it('by setting \'error\'', () => {
+ expect(nextState.error).to.exist;
+ });
+
+ it('by not setting \'loaded\' to false', () => {
+ expect(nextState.loaded).to.be.true;
+ });
+
+ it('by not setting \'user\' to null', () => {
+ expect(nextState.user).to.exist;
+ });
+ });
+}
diff --git a/generated/browser/resources/FullstackPics.js b/generated/browser/resources/FullstackPics.js
new file mode 100644
index 0000000..66d5c6f
--- /dev/null
+++ b/generated/browser/resources/FullstackPics.js
@@ -0,0 +1,32 @@
+'use strict';
+
+const FullstackPics = [
+ 'https://pbs.twimg.com/media/B7gBXulCAAAXQcE.jpg:large',
+ 'https://fbcdn-sphotos-c-a.akamaihd.net/hphotos-ak-xap1/t31.0-8/10862451_10205622990359241_8027168843312841137_o.jpg',
+ 'https://pbs.twimg.com/media/B-LKUshIgAEy9SK.jpg',
+ 'https://pbs.twimg.com/media/B79-X7oCMAAkw7y.jpg',
+ 'https://pbs.twimg.com/media/B-Uj9COIIAIFAh0.jpg:large',
+ 'https://pbs.twimg.com/media/B6yIyFiCEAAql12.jpg:large',
+ 'https://pbs.twimg.com/media/CE-T75lWAAAmqqJ.jpg:large',
+ 'https://pbs.twimg.com/media/CEvZAg-VAAAk932.jpg:large',
+ 'https://pbs.twimg.com/media/CEgNMeOXIAIfDhK.jpg:large',
+ 'https://pbs.twimg.com/media/CEQyIDNWgAAu60B.jpg:large',
+ 'https://pbs.twimg.com/media/CCF3T5QW8AE2lGJ.jpg:large',
+ 'https://pbs.twimg.com/media/CAeVw5SWoAAALsj.jpg:large',
+ 'https://pbs.twimg.com/media/CAaJIP7UkAAlIGs.jpg:large',
+ 'https://pbs.twimg.com/media/CAQOw9lWEAAY9Fl.jpg:large',
+ 'https://pbs.twimg.com/media/B-OQbVrCMAANwIM.jpg:large',
+ 'https://pbs.twimg.com/media/B9b_erwCYAAwRcJ.png:large',
+ 'https://pbs.twimg.com/media/B5PTdvnCcAEAl4x.jpg:large',
+ 'https://pbs.twimg.com/media/B4qwC0iCYAAlPGh.jpg:large',
+ 'https://pbs.twimg.com/media/B2b33vRIUAA9o1D.jpg:large',
+ 'https://pbs.twimg.com/media/BwpIwr1IUAAvO2_.jpg:large',
+ 'https://pbs.twimg.com/media/BsSseANCYAEOhLw.jpg:large',
+ 'https://pbs.twimg.com/media/CJ4vLfuUwAAda4L.jpg:large',
+ 'https://pbs.twimg.com/media/CI7wzjEVEAAOPpS.jpg:large',
+ 'https://pbs.twimg.com/media/CIdHvT2UsAAnnHV.jpg:large',
+ 'https://pbs.twimg.com/media/CGCiP_YWYAAo75V.jpg:large',
+ 'https://pbs.twimg.com/media/CIS4JPIWIAI37qu.jpg:large'
+];
+
+export default FullstackPics;
diff --git a/generated/browser/resources/RandomGreetings.js b/generated/browser/resources/RandomGreetings.js
new file mode 100644
index 0000000..4c66f88
--- /dev/null
+++ b/generated/browser/resources/RandomGreetings.js
@@ -0,0 +1,22 @@
+'use strict';
+
+const greetings = [
+ 'Hello, world!',
+ 'At long last, I live!',
+ 'Hello, simple human.',
+ 'What a beautiful day!',
+ 'I\'m like any other project, except that I am yours. :)',
+ 'This empty string is for Lindsay Levine.',
+ 'こんにちは、ユーザー様。',
+ 'Welcome. To. WEBSITE.',
+ ':D',
+ 'Yes, I think we\'ve met before.',
+ 'Gimme 3 mins... I just grabbed this really dope frittata',
+ 'If Cooper could offer only one piece of advice, it would be to nevSQUIRREL!',
+];
+
+const getRandomFromArray = (arr) => arr[Math.floor(Math.random() * arr.length)];
+
+const getRandomGreeting = () => getRandomFromArray(greetings);
+
+export default getRandomGreeting;
diff --git a/generated/browser/scss/directives/_main.scss b/generated/browser/scss/directives/_main.scss
deleted file mode 100644
index bcc46a0..0000000
--- a/generated/browser/scss/directives/_main.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import "fullstack-logo";
-@import "navbar";
\ No newline at end of file
diff --git a/generated/browser/scss/directives/navbar.scss b/generated/browser/scss/directives/navbar.scss
deleted file mode 100644
index 40e613b..0000000
--- a/generated/browser/scss/directives/navbar.scss
+++ /dev/null
@@ -1,62 +0,0 @@
-navbar {
-
- display: block;
- background: white;
- border-bottom: 1px solid black;
- box-shadow: 1px 1px 14px -3px black;
-
- .container {
- width: 91%;
- max-width: 1340px;
- padding-top: 20px;
- }
-
- fullstack-logo {
- margin-top: 5px;
- margin-right: 20px;
- width: 40px;
- }
-
- a {
- color: black;
- font-size: 16px;
- }
-
- .nav > li > a {
- cursor: pointer;
- transition: color 0.2s;
- &.active {
- color: darken(#f31214, 10%);
- }
- &:hover {
- background: none;
- color: #f31214;
- }
- &:focus {
- background: none;
- }
- }
-
- .login-button {
- float: right;
- border: 1px solid black;
- margin-top: 10px;
- background: none;
- outline: none !important;
- }
-
- .welcome {
- float: right;
- margin-top: 12px;
- span {
- color: black;
- font-size: 16px;
- margin-right: 5px;
- }
- a {
- color: #f30046;
- cursor: pointer;
- }
- }
-
-}
diff --git a/generated/browser/scss/home/main.scss b/generated/browser/scss/home/main.scss
deleted file mode 100644
index f50b10f..0000000
--- a/generated/browser/scss/home/main.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-#home {
- fullstack-logo {
- padding-top: 30px;
- width: 150px;
- margin: 0 auto;
- }
- h1 {
- margin-top: 40px;
- text-align: center;
- }
-}
-
diff --git a/generated/browser/scss/login/main.scss b/generated/browser/scss/login/main.scss
deleted file mode 100644
index 66aa76c..0000000
--- a/generated/browser/scss/login/main.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-#login-form {
- width: 60%;
- margin: 0 auto;
-}
\ No newline at end of file
diff --git a/generated/browser/scss/main.scss b/generated/browser/scss/main.scss
deleted file mode 100644
index 0ea90a3..0000000
--- a/generated/browser/scss/main.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-* {
- box-sizing: border-box;
-}
-
-body {
- background: #fdfdfd;
-}
-
-@mixin clearfix {
- &:after {
- content: "";
- display: table;
- clear: both;
- }
-}
-
-#main {
- padding-top: 60px;
- margin: 0 auto;
-}
-
-@import 'directives/main';
-@import 'home/main';
-@import 'about/main';
-@import 'tutorial/main';
-@import 'login/main';
diff --git a/generated/browser/scss/tutorial/main.scss b/generated/browser/scss/tutorial/main.scss
deleted file mode 100644
index 6c95225..0000000
--- a/generated/browser/scss/tutorial/main.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-@import 'tutorial-section';
-@import 'tutorial-video';
-@import 'tutorial-section-menu';
-
-#tutorial {
-}
\ No newline at end of file
diff --git a/generated/browser/scss/tutorial/tutorial-section-menu.scss b/generated/browser/scss/tutorial/tutorial-section-menu.scss
deleted file mode 100644
index e9c88a0..0000000
--- a/generated/browser/scss/tutorial/tutorial-section-menu.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-tutorial-section-menu {
-
- display: block;
- @include clearfix;
-
- ul {
- margin-left: 23px;
- list-style-type: none;
- font-weight: 300;
- li {
- float: left;
- margin-right: 40px;
- padding: 10px;
- cursor: pointer;
- &:hover {
- background: lighten(gray, 45%);
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/generated/browser/scss/tutorial/tutorial-section.scss b/generated/browser/scss/tutorial/tutorial-section.scss
deleted file mode 100644
index bf98b0c..0000000
--- a/generated/browser/scss/tutorial/tutorial-section.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-tutorial-section {
-
- display: block;
- width: 90%;
- margin: 10px auto;
- padding: 20px 0 60px 0;
-
- & > h1 {
- margin: 20px 0 40px 0;
- text-align: center;
- font-weight: bold;
- }
-
-}
\ No newline at end of file
diff --git a/generated/browser/scss/tutorial/tutorial-video.scss b/generated/browser/scss/tutorial/tutorial-video.scss
deleted file mode 100644
index d0f0f48..0000000
--- a/generated/browser/scss/tutorial/tutorial-video.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-tutorial-video {
-
- display: block;
- width: 600px;
- margin: 0 auto;
-
- h1 {
- font-weight: 300;
- font-size: 24px;
- margin-bottom: 15px;
- }
-
- iframe {
- width: 600px;
- height: 400px;
- }
-
-}
\ No newline at end of file
diff --git a/generated/browser/shared/AuthForm/AuthForm.js b/generated/browser/shared/AuthForm/AuthForm.js
new file mode 100644
index 0000000..4f5b6c7
--- /dev/null
+++ b/generated/browser/shared/AuthForm/AuthForm.js
@@ -0,0 +1,81 @@
+'use strict';
+
+import React, { Component, PropTypes } from 'react'
+import {
+ FormGroup,
+ FormControl,
+ ControlLabel,
+ HelpBlock,
+ Button,
+ Row,
+ Col
+} from 'react-bootstrap'
+
+export default class AuthForm extends Component {
+
+ static propTypes = {
+ buttonLabel: PropTypes.string,
+ buttonStyle: PropTypes.string,
+ onSubmit: PropTypes.func
+ }
+
+ static defaultProps = {
+ buttonStyle: 'default',
+ buttonLabel: 'Submit'
+ }
+
+ constructor (props) {
+ super(props)
+ this.state = {
+ form: {
+ email: '',
+ password: ''
+ }
+ }
+ }
+
+ handleSubmit = (e) => {
+ e.preventDefault()
+ this.props.onSubmit(this.state.form)
+ const newState = {...this.state}
+ newState.form = { email: '', password: '' }
+ this.setState(newState)
+ }
+
+ handleChange = (e, field) => {
+ const newState = {...this.state}
+ newState.form[field] = e.target.value
+ this.setState(newState)
+ }
+
+ render () {
+ const { form } = this.state
+ const { buttonLabel, buttonStyle } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
diff --git a/generated/browser/shared/AuthForm/index.js b/generated/browser/shared/AuthForm/index.js
new file mode 100644
index 0000000..78e48ec
--- /dev/null
+++ b/generated/browser/shared/AuthForm/index.js
@@ -0,0 +1 @@
+export default from './AuthForm';
diff --git a/generated/browser/shared/Logo/Logo.js b/generated/browser/shared/Logo/Logo.js
new file mode 100644
index 0000000..a264216
--- /dev/null
+++ b/generated/browser/shared/Logo/Logo.js
@@ -0,0 +1,14 @@
+'use strict';
+
+import React, { PropTypes } from 'react';
+import './_Logo';
+
+const Logo = (props) => {
+ return (
+
+
+
+ )
+}
+
+export default Logo
diff --git a/generated/browser/scss/directives/fullstack-logo.scss b/generated/browser/shared/Logo/_Logo.scss
similarity index 57%
rename from generated/browser/scss/directives/fullstack-logo.scss
rename to generated/browser/shared/Logo/_Logo.scss
index 28bbec0..a34beaa 100644
--- a/generated/browser/scss/directives/fullstack-logo.scss
+++ b/generated/browser/shared/Logo/_Logo.scss
@@ -1,7 +1,8 @@
-fullstack-logo {
- display: block;
+.logo {
+
img {
display: block;
width: 100%;
}
-}
\ No newline at end of file
+
+}
diff --git a/generated/browser/shared/Logo/index.js b/generated/browser/shared/Logo/index.js
new file mode 100644
index 0000000..0a77052
--- /dev/null
+++ b/generated/browser/shared/Logo/index.js
@@ -0,0 +1 @@
+export default from './Logo';
diff --git a/generated/browser/shared/NotFound/NotFound.js b/generated/browser/shared/NotFound/NotFound.js
new file mode 100644
index 0000000..5c10092
--- /dev/null
+++ b/generated/browser/shared/NotFound/NotFound.js
@@ -0,0 +1,14 @@
+'use strict';
+
+import React from 'react'
+
+const NotFound = (props) => {
+ return (
+
+
404
+ Page Not Found
+
+ )
+}
+
+export default NotFound
diff --git a/generated/browser/shared/NotFound/index.js b/generated/browser/shared/NotFound/index.js
new file mode 100644
index 0000000..50068c5
--- /dev/null
+++ b/generated/browser/shared/NotFound/index.js
@@ -0,0 +1 @@
+export default from './NotFound';
diff --git a/generated/browser/shared/index.js b/generated/browser/shared/index.js
new file mode 100644
index 0000000..26e8d39
--- /dev/null
+++ b/generated/browser/shared/index.js
@@ -0,0 +1,3 @@
+export AuthForm from './AuthForm';
+export Logo from './Logo';
+export NotFound from './NotFound';
diff --git a/generated/browser/utils/index.js b/generated/browser/utils/index.js
new file mode 100644
index 0000000..4e5eaa7
--- /dev/null
+++ b/generated/browser/utils/index.js
@@ -0,0 +1,8 @@
+'use strict';
+
+export function parseJSON (response) {
+ return response.text()
+ .then(text => text ? JSON.parse(text) : {})
+}
+
+export const parseData = res => res.data;
diff --git a/generated/gitignore.txt b/generated/gitignore.txt
index fb0e6f3..f3ec14e 100644
--- a/generated/gitignore.txt
+++ b/generated/gitignore.txt
@@ -1,6 +1,7 @@
.idea
node_modules
npm-debug.log
-public
+build
.DS_Store
coverage
+.nyc_output
diff --git a/generated/gulpfile.js b/generated/gulpfile.js
deleted file mode 100644
index 7463d84..0000000
--- a/generated/gulpfile.js
+++ /dev/null
@@ -1,188 +0,0 @@
-// All used modules.
-var gulp = require('gulp');
-var babel = require('gulp-babel');
-var runSeq = require('run-sequence');
-var plumber = require('gulp-plumber');
-var concat = require('gulp-concat');
-var rename = require('gulp-rename');
-var sass = require('gulp-sass');
-var livereload = require('gulp-livereload');
-var minifyCSS = require('gulp-minify-css');
-var ngAnnotate = require('gulp-ng-annotate');
-var uglify = require('gulp-uglify');
-var sourcemaps = require('gulp-sourcemaps');
-var eslint = require('gulp-eslint');
-var karma = require('karma');
-var mocha = require('gulp-spawn-mocha');
-var istanbul = require('gulp-istanbul');
-var notify = require('gulp-notify');
-
-// Development tasks
-// --------------------------------------------------------------
-
-// Live reload business.
-gulp.task('reload', function () {
- livereload.reload();
-});
-
-gulp.task('reloadCSS', function () {
- return gulp.src('./public/style.css').pipe(livereload());
-});
-
-gulp.task('lintJS', function () {
-
- return gulp.src(['./browser/js/**/*.js', './server/**/*.js'])
- .pipe(plumber({
- errorHandler: notify.onError('Linting FAILED! Check your gulp process.')
- }))
- .pipe(eslint())
- .pipe(eslint.format())
- .pipe(eslint.failOnError());
-
-});
-
-gulp.task('buildJS', ['lintJS'], function () {
- return gulp.src(['./browser/js/app.js', './browser/js/**/*.js'])
- .pipe(plumber())
- .pipe(sourcemaps.init())
- .pipe(concat('main.js'))
- .pipe(babel({
- presets: ['es2015']
- }))
- .pipe(sourcemaps.write())
- .pipe(gulp.dest('./public'));
-});
-
-gulp.task('testServerJS', function () {
- require('babel-register');
- //testing environment variable
- process.env.NODE_ENV = 'testing';
- return gulp.src('./tests/server/**/*.js', {
- read: false
- }).pipe(mocha({ reporter: 'spec' }));
-});
-
-gulp.task('testServerJSWithCoverage', function (done) {
- //testing environment variable
- process.env.NODE_ENV = 'testing';
- gulp.src('./server/**/*.js')
- .pipe(istanbul({
- includeUntested: true
- }))
- .pipe(istanbul.hookRequire())
- .on('finish', function () {
- gulp.src('./tests/server/**/*.js', {read: false})
- .pipe(mocha({reporter: 'spec'}))
- .pipe(istanbul.writeReports({
- dir: './coverage/server/',
- reporters: ['html', 'text']
- }))
- .on('end', done);
- });
-});
-
-gulp.task('testBrowserJS', function (done) {
- //testing environment variable
- process.env.NODE_ENV = 'testing';
- var server = new karma.Server({
- configFile: __dirname + '/tests/browser/karma.conf.js',
- singleRun: true
- }, done);
- server.start();
-});
-
-gulp.task('buildCSS', function () {
-
- var sassCompilation = sass();
- sassCompilation.on('error', console.error.bind(console));
-
- return gulp.src('./browser/scss/main.scss')
- .pipe(plumber({
- errorHandler: notify.onError('SASS processing failed! Check your gulp process.')
- }))
- .pipe(sourcemaps.init())
- .pipe(sassCompilation)
- .pipe(sourcemaps.write())
- .pipe(rename('style.css'))
- .pipe(gulp.dest('./public'));
-});
-
-// Production tasks
-// --------------------------------------------------------------
-
-gulp.task('lintJSProduction', function () {
-
- return gulp.src(['./browser/js/**/*.js', './server/**/*.js'])
- .pipe(plumber({
- errorHandler: notify.onError('Linting FAILED! Check your gulp process.')
- }))
- .pipe(eslint({
- rules: {
- 'no-debugger': 2 // 1 in dev
- }
- }))
- .pipe(eslint.format())
- .pipe(eslint.failOnError());
-
-});
-
-gulp.task('buildCSSProduction', function () {
- return gulp.src('./browser/scss/main.scss')
- .pipe(sass())
- .pipe(rename('style.css'))
- .pipe(minifyCSS())
- .pipe(gulp.dest('./public'))
-});
-
-gulp.task('buildJSProduction', function () {
- return gulp.src(['./browser/js/app.js', './browser/js/**/*.js'])
- .pipe(concat('main.js'))
- .pipe(babel({
- presets: ['es2015']
- }))
- .pipe(ngAnnotate())
- .pipe(uglify())
- .pipe(gulp.dest('./public'));
-});
-
-gulp.task('buildProduction', ['buildCSSProduction', 'buildJSProduction']);
-
-// Composed tasks
-// --------------------------------------------------------------
-
-gulp.task('build', function () {
- if (process.env.NODE_ENV === 'production') {
- runSeq(['buildJSProduction', 'buildCSSProduction']);
- } else {
- runSeq(['buildJS', 'buildCSS']);
- }
-});
-
-gulp.task('default', function () {
-
- gulp.start('build');
-
- // Run when anything inside of browser/js changes.
- gulp.watch('browser/js/**', function () {
- runSeq('buildJS', 'reload');
- });
-
- // Run when anything inside of browser/scss changes.
- gulp.watch('browser/scss/**', function () {
- runSeq('buildCSS', 'reloadCSS');
- });
-
- gulp.watch('server/**/*.js', ['lintJS']);
-
- // Reload when a template (.html) file changes.
- gulp.watch(['browser/**/*.html', 'server/app/views/*.html'], ['reload']);
-
- // Run server tests when a server file or server test file changes.
- gulp.watch(['tests/server/**/*.js', 'server/app/**/*.js'], ['testServerJS']);
-
- // Run browser testing when a browser test file changes.
- gulp.watch('tests/browser/**/*', ['testBrowserJS']);
-
- livereload.listen();
-
-});
diff --git a/generated/package.json b/generated/package.json
index 2aaccfa..a6ac9a2 100644
--- a/generated/package.json
+++ b/generated/package.json
@@ -5,18 +5,30 @@
"main": "server/start.js",
"scripts": {
"start": "nodemon --watch server -e js,html server/start.js",
- "postinstall": "gulp build"
+ "postinstall": "npm run build",
+ "build": "webpack --config webpack.dev.config.js",
+ "build:production": "NODE_ENV='production' webpack -p --config webpack.prod.config.js",
+ "testBrowserJS": "NODE_ENV='testing' ./node_modules/.bin/mocha --compilers js:babel-register ./browser/**/*.test.js",
+ "testBrowserJS:watch": "NODE_ENV='testing' ./node_modules/.bin/mocha -w --compilers js:babel-register ./browser/**/*.test.js",
+ "testServerJS": "NODE_ENV='testing' mocha --compilers js:babel-register ./server/**/*.test.js",
+ "testServerJS:watch": "NODE_ENV='testing' ./node_modules/.bin/mocha -w --compilers js:babel-register ./server**/*.test.js",
+ "test": "NODE_ENV='testing' ./node_modules/.bin/mocha --compilers js:babel-register ./browser/**/*.test.js ./server/**/*.test.js",
+ "cover": "nyc --reporter=lcov --reporter=text --reporter=html --require babel-register npm test"
},
"engines": {
"node": ">=4.0.0"
},
"dependencies": {
- "angular": "^1.5.0-beta.0",
- "angular-animate": "^1.4.7",
- "angular-mocks": "^1.4.0",
- "angular-ui-bootstrap": "^0.14.3",
- "angular-ui-router": "^0.2.15",
- "babel-preset-es2015": "^6.6.0",
+ "autoprefixer": "^6.5.0",
+ "axios": "^0.14.0",
+ "babel-eslint": "^7.0.0",
+ "babel-loader": "^6.2.5",
+ "babel-polyfill": "^6.13.0",
+ "babel-preset-es2015": "^6.14.0",
+ "babel-preset-react": "^6.11.1",
+ "babel-preset-react-hmre": "^1.1.1",
+ "babel-preset-stage-1": "^6.13.0",
+ "babel-preset-stage-2": "^6.13.0",
"babel-register": "^6.6.0",
"bluebird": "^3.3.3",
"body-parser": "^1.12.0",
@@ -25,35 +37,21 @@
"chalk": "^1.0.0",
"connect-session-sequelize": "^3.0.0",
"cookie-parser": "^1.3.4",
+ "css-loader": "^0.25.0",
"eslint": "^3.4.0",
"eslint-config-fullstack": "^1.5.0",
+ "eslint-loader": "^1.5.0",
"express": "^4.12.0",
"express-session": "^1.10.3",
- "gulp": "^3.8.11",
- "gulp-babel": "^6.1.2",
- "gulp-concat": "^2.5.2",
- "gulp-eslint": "^3.0.1",
- "gulp-istanbul": "^0.9.0",
- "gulp-livereload": "^3.7.0",
- "gulp-minify-css": "^0.4.6",
- "gulp-ng-annotate": "^0.5.2",
- "gulp-notify": "^2.2.0",
- "gulp-plumber": "^0.6.6",
- "gulp-rename": "^1.2.0",
- "gulp-sass": "^2.0.4",
- "gulp-sourcemaps": "^1.3.0",
- "gulp-spawn-mocha": "^3.1.0",
- "gulp-uglify": "^1.1.0",
+ "extract-text-webpack-plugin": "^1.0.1",
+ "html-webpack-plugin": "^2.22.0",
"istanbul": "^0.4.5",
- "karma": "^0.13.14",
- "karma-chai": "^0.1.0",
- "karma-chrome-launcher": "0.2.3",
- "karma-coverage": "^0.2.7",
- "karma-mocha": "^0.1.10",
- "karma-mocha-reporter": "^1.0.2",
"lodash": "^3.9.3",
"mocha": "^3.0.2",
+ "nock": "^8.0.0",
+ "node-sass": "^3.10.0",
"nodemon": "^1.3.7",
+ "nyc": "^8.3.0",
"passport": "^0.2.1",
"passport-facebook": "^1.0.3",
"passport-google-oauth": "^0.1.5",
@@ -62,12 +60,31 @@
"pg": "^4.5.5",
"pg-hstore": "^2.3.2",
"pg-native": "^1.10.0",
+ "postcss-loader": "^0.13.0",
+ "precss": "^1.4.0",
+ "react": "^15.3.2",
+ "react-bootstrap": "^0.30.3",
+ "react-dom": "^15.3.2",
+ "react-redux": "^4.4.5",
+ "react-router": "^2.8.1",
+ "react-router-bootstrap": "^0.23.1",
+ "react-router-redux": "^4.0.6",
+ "redux": "^3.6.0",
+ "redux-logger": "^2.6.1",
+ "redux-mock-store": "^1.2.1",
+ "redux-thunk": "^2.1.0",
"run-sequence": "^1.0.2",
+ "sass-loader": "^4.0.2",
"sequelize": "^3.23.3",
"serve-favicon": "^2.2.0",
"sinon": "^1.13.0",
"socket.io": "^1.3.4",
"socket.io-client": "^1.3.5",
- "supertest": "^0.15.0"
+ "style-loader": "^0.13.1",
+ "supertest": "^0.15.0",
+ "webpack": "^1.13.2",
+ "webpack-dev-middleware": "^1.8.3",
+ "webpack-dev-server": "^1.16.1",
+ "webpack-hot-middleware": "^2.12.2"
}
}
diff --git a/generated/server/app/configure/app-variables.js b/generated/server/app/configure/app-variables.js
index 3ddd92d..d729400 100644
--- a/generated/server/app/configure/app-variables.js
+++ b/generated/server/app/configure/app-variables.js
@@ -4,7 +4,8 @@ var chalk = require('chalk');
var util = require('util');
var rootPath = path.join(__dirname, '../../../');
-var indexPath = path.join(rootPath, './server/app/views/index.html');
+var indexTemplatePath = path.join(rootPath, './server/app/views/index.html');
+var indexPath = path.join(rootPath, './build/index.html');
var faviconPath = path.join(rootPath, './server/app/views/favicon.ico');
var env = require(path.join(rootPath, './server/env'));
diff --git a/generated/server/app/configure/index.js b/generated/server/app/configure/index.js
index e5111d0..f6af3f7 100644
--- a/generated/server/app/configure/index.js
+++ b/generated/server/app/configure/index.js
@@ -13,6 +13,7 @@ module.exports = function (app, db) {
require('./app-variables')(app);
require('./static-middleware')(app);
require('./parsing-middleware')(app);
+ require('./webpack-middleware')(app);
// Logging middleware, set as application
// variable inside of server/app/configure/app-variables.js
diff --git a/generated/server/app/configure/static-middleware.js b/generated/server/app/configure/static-middleware.js
index f1a6b9b..1d80fbe 100644
--- a/generated/server/app/configure/static-middleware.js
+++ b/generated/server/app/configure/static-middleware.js
@@ -9,11 +9,13 @@ module.exports = function (app) {
var npmPath = path.join(root, './node_modules');
var publicPath = path.join(root, './public');
+ var buildPath = path.join(root, './build');
var browserPath = path.join(root, './browser');
app.use(favicon(app.getValue('faviconPath')));
app.use(express.static(npmPath));
app.use(express.static(publicPath));
+ app.use(express.static(buildPath));
app.use(express.static(browserPath));
};
diff --git a/generated/server/app/configure/webpack-middleware.js b/generated/server/app/configure/webpack-middleware.js
new file mode 100644
index 0000000..1d42320
--- /dev/null
+++ b/generated/server/app/configure/webpack-middleware.js
@@ -0,0 +1,20 @@
+'use strict';
+
+const path = require('path');
+const NODE_ENV = process.env.NODE_ENV;
+
+module.exports = function (app) {
+ if (NODE_ENV !== 'production' && NODE_ENV !== 'testing') {
+ const webpack = require('webpack');
+ const webpackHotMiddleware = require('webpack-hot-middleware');
+ const webpackDevMiddleware = require('webpack-dev-middleware');
+ const config = require(path.join(app.getValue('projectRoot'), 'webpack.dev.config.js'));
+ const compiler = webpack(config);
+
+ app.use(webpackHotMiddleware(compiler));
+ app.use(webpackDevMiddleware(compiler, {
+ noInfo: true,
+ publicPath: config.output.publicPath
+ }))
+ }
+}
diff --git a/generated/server/app/views/index.html b/generated/server/app/views/index.html
index a5e18ae..6974e14 100644
--- a/generated/server/app/views/index.html
+++ b/generated/server/app/views/index.html
@@ -4,18 +4,8 @@
Fullstack Academy Generated Application
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/generated/tests/server/.eslintrc.json b/generated/server/tests/server/.eslintrc.json
similarity index 100%
rename from generated/tests/server/.eslintrc.json
rename to generated/server/tests/server/.eslintrc.json
diff --git a/generated/tests/server/models/user-test.js b/generated/server/tests/server/models/user.test.js
similarity index 100%
rename from generated/tests/server/models/user-test.js
rename to generated/server/tests/server/models/user.test.js
diff --git a/generated/tests/server/routes/members-only-test.js b/generated/server/tests/server/routes/members-only.test.js
similarity index 100%
rename from generated/tests/server/routes/members-only-test.js
rename to generated/server/tests/server/routes/members-only.test.js
diff --git a/generated/tests/browser/.eslintrc.json b/generated/tests/browser/.eslintrc.json
deleted file mode 100644
index a3336db..0000000
--- a/generated/tests/browser/.eslintrc.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "env": {
- "browser": true,
- "mocha": true
- },
- "globals": {
- "app": true,
- "_": true,
- "module": true,
- "inject": true,
- "expect": true,
- "sinon": true
- }
-}
diff --git a/generated/tests/browser/fsa-prebuilt/auth-service-test.js b/generated/tests/browser/fsa-prebuilt/auth-service-test.js
deleted file mode 100644
index 25ff2d5..0000000
--- a/generated/tests/browser/fsa-prebuilt/auth-service-test.js
+++ /dev/null
@@ -1,288 +0,0 @@
-describe('AuthService', function () {
-
- beforeEach(module('fsaPreBuilt'));
-
- var $httpBackend;
- var $rootScope;
- beforeEach('Get tools', inject(function (_$httpBackend_, _$rootScope_) {
- $httpBackend = _$httpBackend_;
- $rootScope = _$rootScope_;
- }));
-
- var AUTH_EVENTS;
- var AuthService;
- var Session;
- beforeEach('Get factories', inject(function (_AuthService_, _Session_, _AUTH_EVENTS_) {
- AuthService = _AuthService_;
- Session = _Session_;
- AUTH_EVENTS = _AUTH_EVENTS_;
- Session.destroy();
- }));
-
- it('should be an object', function () {
- expect(AuthService).to.be.an('object');
- });
-
- describe('isAuthenicated', function () {
-
- it('should return true if a Session exists', function () {
- Session.create({email: 'cool@gmail.com'});
- expect(AuthService.isAuthenticated()).to.be.ok;
- });
-
- it('should return false if a Session does not exist', function () {
- Session.destroy();
- expect(AuthService.isAuthenticated()).to.not.be.ok;
- });
-
- });
-
- describe('getLoggedInUser', function () {
-
- it('should return the user from the Session if already authenticated', function (done) {
- var x = {};
- Session.create(x);
- AuthService.getLoggedInUser().then(function (user) {
- expect(user).to.be.equal(x);
- done();
- });
- $rootScope.$digest(); // In order to resolve $q promise.
- });
-
- describe('when user not already authenticated', function () {
-
- afterEach(function () {
- $httpBackend.verifyNoOutstandingExpectation();
- $httpBackend.verifyNoOutstandingRequest();
- });
-
- it('should make a request to GET /session', function (done) {
-
- $httpBackend.expectGET('/session');
- $httpBackend.whenGET('/session').respond({user: {}});
-
- AuthService.getLoggedInUser().then(function () {
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- describe('on successful response', function () {
-
- var potus = {
- email: 'obama@gmail.com', president: true
- };
-
- beforeEach(function () {
- $httpBackend.whenGET('/session').respond({user: potus});
- });
-
- it('should resolve to the responded user from /session', function (done) {
-
- AuthService.getLoggedInUser().then(function (user) {
- expect(user).to.be.deep.equal(potus);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should set the session', function (done) {
-
- AuthService.getLoggedInUser().then(function (user) {
- expect(Session.user).to.be.deep.equal(potus);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should fire off AUTH_EVENTS.loginSuccess', function (done) {
-
- var spy = sinon.spy();
-
- $rootScope.$on(AUTH_EVENTS.loginSuccess, spy);
-
- AuthService.getLoggedInUser().then(function () {
- expect(spy.called).to.be.ok;
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- });
-
- it('should resolve to the reponse from /session if it is OK', function (done) {
-
- var potus = {
- email: 'obama@gmail.com', president: true
- };
-
- $httpBackend.whenGET('/session').respond({user: potus});
-
- AuthService.getLoggedInUser().then(function (user) {
- expect(user).to.be.deep.equal(potus);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should resolve to null if the response was 401/erroneous', function (done) {
-
- $httpBackend.whenGET('/session').respond(401);
-
- AuthService.getLoggedInUser().then(function (user) {
- expect(user).to.be.equal(null);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- });
-
- });
-
- describe('login', function () {
-
- afterEach(function () {
- $httpBackend.verifyNoOutstandingExpectation();
- $httpBackend.verifyNoOutstandingRequest();
- });
-
- it('should make a request POST /login with the given data', function (done) {
-
- var potus = {
- email: 'obama@gmail.com',
- password: 'potus'
- };
-
- $httpBackend.expectPOST('/login', potus).respond(200, {});
-
- AuthService.login(potus).then(done);
-
- $httpBackend.flush();
-
- });
-
- it('should forward invalid credentials error on unsuccessful response', function (done) {
-
- $httpBackend.expectPOST('/login').respond(401);
-
- AuthService.login({ email: 'fakedude@gmail.com', password: 'nop' }).catch(function (err) {
- expect(err.message).to.be.equal('Invalid login credentials.');
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- describe('on successful response', function () {
-
- var user = {email: 'coolguy@beans.com'};
- var login = {email: 'coolguy@beans.com', password: 'sweetjuicy'};
-
- beforeEach(function () {
- $httpBackend.whenPOST('/login').respond({user: user});
- });
-
- it('should resolve to the responded user', function (done) {
-
- AuthService.login(login).then(function (user) {
- expect(user).to.be.deep.equal(user);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should set Session', function (done) {
-
- Session.destroy();
-
- AuthService.login(login).then(function (user) {
- expect(Session.user).to.be.deep.equal(user);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should fire off AUTH_EVENTS.loginSuccess', function (done) {
-
- var spy = sinon.spy();
-
- $rootScope.$on(AUTH_EVENTS.loginSuccess, spy);
-
- AuthService.login(login).then(function () {
- expect(spy.called).to.be.ok;
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- });
-
- });
-
- describe('logout', function () {
-
- beforeEach(function () {
- $httpBackend.expectGET('/logout').respond(200);
- });
-
- afterEach(function () {
- $httpBackend.verifyNoOutstandingExpectation();
- $httpBackend.verifyNoOutstandingRequest();
- });
-
- it('should make a request GET /logout', function (done) {
- AuthService.logout().then(done);
- $httpBackend.flush();
- });
-
- it('should destroy the session', function (done) {
-
- Session.create({ email: 'obama@gmai.com' });
-
- AuthService.logout().then(function () {
- expect(Session.user).to.be.equal(null);
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- it('should broadcast the logoutSuccess AUTH_EVENT', function (done) {
-
- var spy = sinon.spy();
-
- $rootScope.$on(AUTH_EVENTS.logoutSuccess, spy);
-
- AuthService.logout().then(function () {
- expect(spy.called).to.be.ok;
- done();
- });
-
- $httpBackend.flush();
-
- });
-
- });
-
-});
diff --git a/generated/tests/browser/fsa-prebuilt/session-test.js b/generated/tests/browser/fsa-prebuilt/session-test.js
deleted file mode 100644
index 020522d..0000000
--- a/generated/tests/browser/fsa-prebuilt/session-test.js
+++ /dev/null
@@ -1,76 +0,0 @@
-describe('Session Service', function () {
-
- beforeEach(module('fsaPreBuilt'));
-
- var $rootScope;
- beforeEach(inject(function (_$rootScope_) {
- $rootScope = _$rootScope_;
- }));
-
- var Session;
- var AUTH_EVENTS;
- beforeEach(inject(function ($injector) {
- Session = $injector.get('Session');
- AUTH_EVENTS = $injector.get('AUTH_EVENTS');
- }));
-
- it('should be an object', function () {
- expect(Session).to.be.an('object');
- });
-
- it('should by default have user as null', function () {
- expect(Session.user).to.be.equal(null);
- });
-
- describe('create method', function () {
-
- it('should when called with a user argument' +
- 'set the user to session', function () {
- var user = {};
- Session.create(user);
- expect(Session.user).to.be.equal(user);
- });
-
- });
-
- describe('destroy method', function () {
-
- it('should set user to null', function () {
-
- Session.user = {};
-
- Session.destroy();
-
- expect(Session.user).to.be.equal(null);
-
- });
-
- });
-
- describe('event listening', function () {
-
- it('should call destroy when notAuthenticated event is fired', function () {
-
- var spy = sinon.spy(Session, 'destroy');
-
- $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
-
- expect(spy.called).to.be.ok;
- spy.restore();
-
- });
-
- it('should call destroy when sessionTimeout event is fired', function () {
-
- var spy = sinon.spy(Session, 'destroy');
-
- $rootScope.$broadcast(AUTH_EVENTS.sessionTimeout);
-
- expect(spy.called).to.be.ok;
- spy.restore();
-
- });
-
- });
-
-});
diff --git a/generated/tests/browser/karma.conf.js b/generated/tests/browser/karma.conf.js
deleted file mode 100644
index 107785a..0000000
--- a/generated/tests/browser/karma.conf.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* eslint-env node */
-var path = require('path');
-
-module.exports = function (config) {
-
- var filesCollection = [
- 'node_modules/lodash/index.js',
- 'node_modules/angular/angular.js',
- 'node_modules/angular-animate/angular-animate.js',
- 'node_modules/angular-ui-router/release/angular-ui-router.js',
- 'node_modules/angular-ui-bootstrap/ui-bootstrap.js',
- 'node_modules/angular-ui-bootstrap/ui-bootstrap-tpls.js',
- 'node_modules/socket.io-client/socket.io.js',
- 'public/main.js',
- 'node_modules/sinon/pkg/sinon.js',
- 'node_modules/angular-mocks/angular-mocks.js',
- 'tests/browser/**/*.js'
- ];
-
- var excludeFiles = [
- 'tests/browser/karma.conf.js'
- ];
-
- var configObj = {
- browsers: ['Chrome'],
- frameworks: ['mocha', 'chai'],
- basePath: path.join(__dirname, '../../'),
- files: filesCollection,
- exclude: excludeFiles,
- reporters: ['mocha', 'coverage'],
- preprocessors: {
- 'public/main.js': 'coverage'
- },
- coverageReporter: {
- dir: 'coverage/browser/',
- reporters: [{
- type: 'text',
- subdir: '.'
- }, {
- type: 'html',
- subdir: '.'
- }]
- }
- };
-
- config.set(configObj);
-
-};
diff --git a/generated/webpack.dev.config.js b/generated/webpack.dev.config.js
new file mode 100644
index 0000000..ea2699a
--- /dev/null
+++ b/generated/webpack.dev.config.js
@@ -0,0 +1,63 @@
+'use strict'
+
+const path = require('path');
+const webpack = require('webpack');
+const autoprefixer = require('autoprefixer');
+const precss = require('precss');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const indexPath = path.join(__dirname, './server', 'app', 'views', 'index.html');
+
+module.exports = {
+ devtool: 'eval',
+ entry: [
+ 'babel-polyfill',
+ 'webpack-hot-middleware/client',
+ './browser/app.js'
+ ],
+ output: {
+ path: path.join(__dirname, 'build'),
+ filename: 'bundle.js',
+ publicPath: '/'
+ },
+ resolve: {
+ extensions: ['', '.js', '.jsx', '.css', '.scss'],
+ modulesDirectories: ['browser', 'node_modules']
+ },
+ module: {
+ preloaders: [
+ {
+ test: /\.jsx?$/,
+ loaders: ['eslint']
+ }
+ ],
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: 'babel',
+ exclude: /(node_modules)|(bower_components)/
+ },
+ {
+ test: /\.s?css$/,
+ loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss!sass?sourceMap'),
+ include: /(browser)|(node_modules)/
+ }
+ ]
+ },
+ postcss: function () {
+ return [autoprefixer, precss]
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ inject: true,
+ template: indexPath
+ }),
+ new webpack.NoErrorsPlugin(),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify('development')
+ }),
+ new webpack.HotModuleReplacementPlugin(),
+ new ExtractTextPlugin('style.css', { allChunks: true })
+ ]
+}
+
diff --git a/generated/webpack.prod.config.js b/generated/webpack.prod.config.js
new file mode 100644
index 0000000..d9fdad7
--- /dev/null
+++ b/generated/webpack.prod.config.js
@@ -0,0 +1,85 @@
+'use strict';
+
+const path = require('path');
+const webpack = require('webpack');
+const autoprefixer = require('autoprefixer');
+const precss = require('precss');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const indexPath = path.join(__dirname, './server', 'app', 'views', 'index.html');
+
+module.exports = {
+ devtool: 'source-map',
+ entry: [
+ 'babel-polyfill',
+ './browser/app.js'
+ ],
+ output: {
+ path: path.join(__dirname, 'build'),
+ filename: 'bundle.js',
+ publicPath: '/'
+ },
+ resolve: {
+ extensions: ['', '.js', '.jsx', '.css', '.scss'],
+ modulesDirectories: ['browser', 'node_modules']
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: 'babel',
+ exclude: /(node_modules)|(bower_components)/
+ },
+ {
+ test: /\.s?css$/,
+ loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss!sass'),
+ include: /(browser)|(node_modules)/
+ }
+ ]
+ },
+ postcss: function () {
+ return [autoprefixer, precss]
+ },
+ plugins: [
+ new HtmlWebpackPlugin({
+ inject: true,
+ template: indexPath,
+ minify: {
+ removeComments: true,
+ collapseWhitespace: true,
+ removeRedundantAttributes: true,
+ useShortDoctype: true,
+ removeEmptyAttributes: true,
+ removeStyleLinkTypeAttributes: true,
+ keepClosingSlash: true,
+ minifyJS: true,
+ minifyCSS: true,
+ minifyURLs: true
+ }
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify('production')
+ }),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.UglifyJsPlugin({
+ compressor: {
+ screw_ie8: true,
+ warnings: false
+ },
+ minimize: true,
+ compress: {
+ screw_ie8: true,
+ warnings: false
+ },
+ mangle: {
+ screw_ie8: true
+ },
+ output: {
+ comments: false,
+ screw_ie8: true
+ }
+ }),
+ new ExtractTextPlugin('style.css', { allChunks: true })
+ ]
+}
+