diff --git a/.eslintrc b/.eslintrc
index 2b091631d..a629f53c1 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,4 +1,5 @@
-{ "extends": "eslint-config-airbnb",
+{
+ "extends": "eslint-config-airbnb",
"env": {
"browser": true,
"node": true,
@@ -36,6 +37,10 @@
"__SERVER__": true,
"__DISABLE_SSR__": true,
"__DEVTOOLS__": true,
+ "__META__": {},
+ "__API_ENDPOINT__": '',
+ "__API_HOST__": '',
+ "__API_PORT__": '',
"socket": true,
"webpackIsomorphicTools": true
}
diff --git a/.gitignore b/.gitignore
index a7a8d08ee..097716ab4 100755
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ dist/
webpack-assets.json
webpack-stats.json
npm-debug.log
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
index fd6e5d5c5..0c5c083a2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,4 +15,4 @@ before_script:
script:
- npm run lint
- npm test
- - npm run test-node
+ - npm test-node
diff --git a/api/api.js b/api/api.js
index 7cee64671..d793877fd 100644
--- a/api/api.js
+++ b/api/api.js
@@ -1,13 +1,15 @@
import express from 'express';
import session from 'express-session';
import bodyParser from 'body-parser';
-import config from '../src/config';
import * as actions from './actions/index';
import {mapUrl} from 'utils/url.js';
import PrettyError from 'pretty-error';
import http from 'http';
import SocketIo from 'socket.io';
+const apiPort = process.env.APIPORT;
+const apiHost = process.env.APIHOST || 'localhost';
+
const pretty = new PrettyError();
const app = express();
@@ -24,9 +26,7 @@ app.use(session({
}));
app.use(bodyParser.json());
-
app.use((req, res) => {
-
const splittedUrlPath = req.url.split('?')[0].split('/').slice(1);
const {action, params} = mapUrl(actions, splittedUrlPath);
@@ -57,13 +57,13 @@ const bufferSize = 100;
const messageBuffer = new Array(bufferSize);
let messageIndex = 0;
-if (config.apiPort) {
- const runnable = app.listen(config.apiPort, (err) => {
+if (apiPort) {
+ const runnable = app.listen(apiPort, (err) => {
if (err) {
console.error(err);
}
- console.info('----\n==> ๐ API is running on port %s', config.apiPort);
- console.info('==> ๐ป Send requests to http://%s:%s', config.apiHost, config.apiPort);
+ console.info('----\n==> ๐ API is running on port %s', apiPort);
+ console.info('==> ๐ป Send requests to http://%s:%s', apiHost, apiPort);
});
io.on('connection', (socket) => {
@@ -87,7 +87,6 @@ if (config.apiPort) {
});
});
io.listen(runnable);
-
} else {
console.error('==> ERROR: No PORT environment variable has been specified');
}
diff --git a/api/utils/url.js b/api/utils/url.js
index 6e3e78ca8..ec2d8ca68 100644
--- a/api/utils/url.js
+++ b/api/utils/url.js
@@ -1,5 +1,4 @@
export function mapUrl(availableActions = {}, url = []) {
-
const notFound = {action: null, params: []};
// test for empty input
diff --git a/bin/server.js b/bin/server.js
deleted file mode 100644
index d4792622e..000000000
--- a/bin/server.js
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env node
-require('../server.babel'); // babel registration (runtime transpilation for node)
-var path = require('path');
-var rootDir = path.resolve(__dirname, '..');
-/**
- * Define isomorphic constants.
- */
-global.__CLIENT__ = false;
-global.__SERVER__ = true;
-global.__DISABLE_SSR__ = false; // <----- DISABLES SERVER SIDE RENDERING FOR ERROR DEBUGGING
-global.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production';
-
-if (__DEVELOPMENT__) {
- if (!require('piping')({
- hook: true,
- ignore: /(\/\.|~$|\.json|\.scss$)/i
- })) {
- return;
- }
-}
-
-// https://github.com/halt-hammerzeit/webpack-isomorphic-tools
-var WebpackIsomorphicTools = require('webpack-isomorphic-tools');
-global.webpackIsomorphicTools = new WebpackIsomorphicTools(require('../webpack/webpack-isomorphic-tools'))
- .development(__DEVELOPMENT__)
- .server(rootDir, function() {
- require('../src/server');
- });
diff --git a/bin/start.js b/bin/start.js
new file mode 100644
index 000000000..a0660c279
--- /dev/null
+++ b/bin/start.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+require('../server.babel'); // babel registration (runtime transpilation for node)
+require('./start_es6.js');
diff --git a/bin/start_es6.js b/bin/start_es6.js
new file mode 100644
index 000000000..72455b471
--- /dev/null
+++ b/bin/start_es6.js
@@ -0,0 +1,60 @@
+#!/usr/bin/env node
+import path from 'path';
+import http from 'http';
+import renderer from 'universal-redux';
+import httpProxy from 'http-proxy';
+import config from '../config/universal-redux.config.js';
+import SocketIo from 'socket.io';
+
+const isProduction = process.env.NODE_ENV !== 'production';
+const apiPort = process.env.APIPORT;
+const apiHost = process.env.APIHOST || 'localhost';
+const apiEndpoint = '/api';
+
+function setupProxy(app) {
+
+ const proxy = httpProxy.createProxyServer({
+ target: 'http://' + apiHost + ':' + apiPort,
+ ws: true
+ });
+
+ // Proxy to API server
+ app.use(`${apiEndpoint}`, (req, res) => {
+ proxy.web(req, res);
+ });
+
+ // added the error handling to avoid https://github.com/nodejitsu/node-http-proxy/issues/527
+ proxy.on('error', (error, req, res) => {
+ let json;
+ if (error.code !== 'ECONNRESET') {
+ console.error('proxy error', error);
+ }
+ if (!res.headersSent) {
+ res.writeHead(500, {'content-type': 'application/json'});
+ }
+
+ json = {error: 'proxy_error', reason: error.message};
+ res.end(JSON.stringify(json));
+ });
+}
+
+const app = renderer.app();
+
+setupProxy(app);
+
+renderer.setup(config);
+
+const server = new http.Server(app);
+
+if (!isProduction) {
+ const io = new SocketIo(server);
+ io.path(`${__API_ENDPOINT__}/ws`);
+}
+
+server.listen(config.server.port, (err) => {
+ if (err) {
+ console.error(err);
+ }
+ console.info('==> ๐ API calls will be received at:', config.server.host + ':' + config.server.port + apiEndpoint);
+ console.info('==> ๐ป Open http://localhost:%s in a browser to view the app.', config.server.port);
+});
diff --git a/config/meta.config.js b/config/meta.config.js
new file mode 100644
index 000000000..e6539aa75
--- /dev/null
+++ b/config/meta.config.js
@@ -0,0 +1,22 @@
+module.exports = {
+ title: 'React Redux Example',
+ description: 'All the modern best practices in one example.',
+ meta: {
+ charSet: 'utf-8',
+ property: {
+ 'og:site_name': 'React Redux Example',
+ 'og:image': 'https://react-redux.herokuapp.com/logo.jpg',
+ 'og:locale': 'en_US',
+ 'og:title': 'React Redux Example',
+ 'og:description': 'All the modern best practices in one example.',
+ 'twitter:card': 'summary',
+ 'twitter:site': '@erikras',
+ 'twitter:creator': '@erikras',
+ 'twitter:title': 'React Redux Example',
+ 'twitter:description': 'All the modern best practices in one example.',
+ 'twitter:image': 'https://react-redux.herokuapp.com/logo.jpg',
+ 'twitter:image:width': '200',
+ 'twitter:image:height': '200'
+ }
+ }
+}
diff --git a/config/universal-redux.config.js b/config/universal-redux.config.js
new file mode 100644
index 000000000..183c07527
--- /dev/null
+++ b/config/universal-redux.config.js
@@ -0,0 +1,184 @@
+/* eslint-disable */
+const path = require('path');
+const isProduction = process.env.NODE_ENV === 'production';
+const projectRoot = path.resolve(__dirname, '..');
+const sourceRoot = path.resolve(__dirname, '../src');
+const apiPort = process.env.APIPORT;
+const apiHost = process.env.APIHOST || 'localhost';
+const meta = require('./meta.config.js');
+
+module.exports = Object.assign({
+
+ /*
+ // Express configuration
+ */
+ server: {
+ /*
+ // The host to run the Express universal renderer. See src/server.js.
+ //
+ // Expects: String
+ */
+ host: process.env.HOST || 'localhost',
+
+ /*
+ // The port to run Express universal renderer will run on. See src/server.js.
+ //
+ // Expects: Number
+ */
+ port: process.env.PORT,
+ },
+
+ /*
+ // Globals available to both serverside and clientside rendering.
+ // You may also add your own here.
+ */
+ globals: {
+
+ /*
+ // Whether or not to run redux-logger
+ //
+ // Expects: Boolean
+ */
+ __LOGGER__: false,
+
+ /*
+ // Whether or not to run redux-devtools
+ //
+ // Expects: Boolean
+ */
+ __DEVTOOLS__: !isProduction,
+
+ __API_ENDPOINT__: '/api',
+ __API_PORT__: apiPort,
+ __API_HOST__: apiHost,
+ __META__: meta
+ },
+
+ /*
+ // Enable eslint checks per Webpack build. Will not be run
+ // on production.
+ //
+ // Expects: Boolean
+ */
+ lint: {
+ enabled: true,
+ config: projectRoot + '/.eslintrc'
+ },
+
+ /*
+ // Enable native desktop notifications for Webpack build events.
+ // Will not be run on production.
+ //
+ // Expects: Boolean
+ */
+ notifications: false,
+
+ /*
+ // Path to a file with customizations for the default
+ // webpack-isomorphic-tools configuration. Optional.
+ //
+ // Expects: String
+ */
+ toolsConfigPath: __dirname + '/webpack-isomorphic-tools.config.js',
+
+ /*
+ // When eneabled, will output Webpack and Webpack Isomorphic
+ // Tools configurations at startup
+ //
+ // Expects: Boolean
+ */
+ verbose: true,
+
+ /*
+ // The react-router Routes file, Required. Will be added to Webpack aliases.
+ */
+ routes: sourceRoot + '/routes.js',
+
+ redux: {
+ /*
+ // The path to the index of your Redux reducers. Required. Will be added
+ // to Webpcak aliases.
+ */
+ reducers: sourceRoot + '/redux/modules/reducer.js',
+
+ /*
+ // A path to an index of middleware functions. On the serverside, these will
+ // be called with the Express request and response. Optional.
+ //
+ // Expects: String
+ */
+ middleware: sourceRoot + '/redux/middleware/index.js'
+ },
+
+ /*
+ // The path to your replacement for the default HTML shell. Optional.
+ // If not provided, the default used will be that in src/helpers/Html.js.
+ // Will be added to Webpack aliases.
+ */
+ // htmlShell: sourceRoot + '/helpers/Html.js',
+
+ webpack: {
+
+ /*
+ // Whether to merge into the default webpack configuration using
+ // webpack-config-merger.
+ //
+ // If the `merge` parameter is `true`, properties with the same name
+ // will be overwritten. Arrays will be concatenated. Objects will
+ // be merged.
+ //
+ // If the `merge` parameter is `false`, default webpack settings
+ // will not be used and the config specified here will need to
+ // be the complete settings required for building.
+ */
+ merge: true,
+
+ /*
+ // Webpack configuration cusomtizations. There are more parameters
+ // available than specified here. For the full list, see
+ // https://webpack.github.io/docs/configuration.html.
+ */
+ config: {
+
+ /*
+ // The Webpack devtool configuration. May affect build times.
+ // See https://webpack.github.io/docs/configuration.html#devtool
+ */
+ devtool: 'inline-eval-cheap-source-map',
+
+ entry: {
+ main: [
+ 'bootstrap-sass!' + sourceRoot + '/theme/bootstrap.config' + (isProduction ? '.prod' : '') + '.js',
+ 'font-awesome-webpack!' + sourceRoot + '/theme/font-awesome.config' + (isProduction ? '.prod' : '') + '.js'
+ ]
+ },
+
+ /*
+ // Not recommended to change.
+ */
+ context: projectRoot,
+
+ /*
+ // Not recommended to change.
+ */
+ output: {
+
+ /*
+ // Not recommended to change.
+ */
+ path: projectRoot + '/static/dist'
+
+ },
+
+ resolve: {
+
+ /*
+ // Not recommended to change.
+ */
+ root: sourceRoot
+ }
+ }
+ }
+
+});
+/* eslint-enable */
diff --git a/config/webpack-isomorphic-tools.config.js b/config/webpack-isomorphic-tools.config.js
new file mode 100644
index 000000000..cee7bcd0a
--- /dev/null
+++ b/config/webpack-isomorphic-tools.config.js
@@ -0,0 +1,32 @@
+const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');
+
+module.exports = {
+ assets: {
+ // this whole "bootstrap" asset type is only used once in development mode.
+ // the only place it's used is the Html.js file
+ // where a tag is created with the contents of the
+ // './src/theme/bootstrap.config.js' file.
+ // (the aforementioned tag can reduce the white flash
+ // when refreshing page in development mode)
+ //
+ // hooking into 'js' extension require()s isn't the best solution
+ // and I'm leaving this comment here in case anyone finds a better idea.
+ bootstrap: {
+ extension: 'js',
+ include: ['./src/theme/bootstrap.config.js'],
+ filter: (module, regex, options, log) => {
+ function isBootstrapStyle(name) {
+ return name.indexOf('./src/theme/bootstrap.config.js') >= 0;
+ }
+ if (options.development) {
+ return isBootstrapStyle(module.name) && WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);
+ }
+ // no need for it in production mode
+ },
+ // in development mode there's webpack "style-loader",
+ // so the module.name is not equal to module.name
+ path: WebpackIsomorphicToolsPlugin.style_loader_path_extractor,
+ parser: WebpackIsomorphicToolsPlugin.css_loader_parser
+ }
+ }
+};
diff --git a/docs/Ducks.md b/docs/Ducks.md
deleted file mode 100644
index 69cffa405..000000000
--- a/docs/Ducks.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This document has found [another, hopefully permanent, home](https://github.com/erikras/ducks-modular-redux).
-
-Quack.
diff --git a/docs/InlineStyles.md b/docs/InlineStyles.md
deleted file mode 100644
index 01925883d..000000000
--- a/docs/InlineStyles.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Inline Styles
-
-In the long term, CSS, LESS and SASS are dead. To keep this project on the bleeding edge, we should drop SASS support in favor of inline styles.
-
-## Why?
-
-I think the case is made pretty strongly in these three presentations.
-
-Christopher Chedeau | Michael Chan | Colin Megill
---- | --- | ---
-[](http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html) | [](https://www.youtube.com/watch?v=ERB1TJBn32c) | [](https://www.youtube.com/watch?v=NoaxsCi13yQ)
-
-Clearly this is the direction in which web development is moving.
-
-## Why not?
-
-At the moment, all the inline CSS libraries suffer from some or all of these problems:
-
-* Client side only
-* No vendor auto prefixing (requires `User-Agent` checking on server side)
-* No server side media queries, resulting in a flicker on load to adjust to client device width
-
-Ideally, a library would allow for all the benefits of inline calculable styles, but, in production, would allow some generation of a CSS block, with media queries to handle device width conditionals, to be inserted into the page with a `