We\'re here to put a dent in the universe. Otherwise why else even be here?
Each type of visual aid has pros and cons that must be evaluated to ensure it will be beneficial to the overall presentation. Before incorporating visual aids into speeches, the speaker should understand that if used incorrectly, the visual will not be an aid, but a distraction. Planning ahead is important when using visual aids.
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/internals/templates/asyncInjectors.js:
--------------------------------------------------------------------------------
1 | import createReducer from 'reducers.js';
2 |
3 | /**
4 | * Inject an asynchronously loaded reducer
5 | */
6 | export function injectAsyncReducer(store) {
7 | return (name, asyncReducer) => {
8 | store.asyncReducers[name] = asyncReducer; // eslint-disable-line
9 | store.replaceReducer(createReducer(store.asyncReducers));
10 | };
11 | }
12 |
13 | /**
14 | * Inject an asynchronously loaded saga
15 | */
16 | export function injectAsyncSagas(store) {
17 | return (sagas) => sagas.map(store.runSaga);
18 | }
19 |
20 | /**
21 | * Helper for creating injectors
22 | */
23 | export function getAsyncInjectors(store) {
24 | return {
25 | injectReducer: injectAsyncReducer(store),
26 | injectSagas: injectAsyncSagas(store),
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/internals/templates/asyncInjectors.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test async injectors
3 | */
4 |
5 | import expect from 'expect';
6 | import configureStore from 'store.js';
7 | import { memoryHistory } from 'react-router';
8 | import { put } from 'redux-saga/effects';
9 | import { fromJS } from 'immutable';
10 |
11 | import {
12 | injectAsyncReducer,
13 | injectAsyncSagas,
14 | getAsyncInjectors,
15 | } from 'utils/asyncInjectors';
16 |
17 | // Fixtures
18 |
19 | const initialState = fromJS({ reduced: 'soon' });
20 |
21 | const reducer = (state = initialState, action) => {
22 | switch (action.type) {
23 | case 'TEST':
24 | return state.set('reduced', action.payload);
25 | default:
26 | return state;
27 | }
28 | };
29 |
30 | const sagas = [
31 | function* testSaga() {
32 | yield put({ type: 'TEST', payload: 'yup' });
33 | },
34 | ];
35 |
36 | describe('asyncInjectors', () => {
37 | let store;
38 |
39 | describe('getAsyncInjectors', () => {
40 | before(() => {
41 | store = configureStore({}, memoryHistory);
42 | });
43 |
44 | it('given a store, should return all async injectors', () => {
45 | const { injectReducer, injectSagas } = getAsyncInjectors(store);
46 |
47 | injectReducer('test', reducer);
48 | injectSagas(sagas);
49 |
50 | const actual = store.getState().get('test');
51 | const expected = initialState.merge({ reduced: 'yup' });
52 |
53 | expect(actual.toJS()).toEqual(expected.toJS());
54 | });
55 | });
56 |
57 | describe('helpers', () => {
58 | before(() => {
59 | store = configureStore({}, memoryHistory);
60 | });
61 |
62 | describe('injectAsyncReducer', () => {
63 | it('given a store, it should provide a function to inject a reducer', () => {
64 | const injectReducer = injectAsyncReducer(store);
65 |
66 | injectReducer('test', reducer);
67 |
68 | const actual = store.getState().get('test');
69 | const expected = initialState;
70 |
71 | expect(actual.toJS()).toEqual(expected.toJS());
72 | });
73 | });
74 |
75 | describe('injectAsyncSagas', () => {
76 | it('given a store, it should provide a function to inject a saga', () => {
77 | const injectSagas = injectAsyncSagas(store);
78 |
79 | injectSagas(sagas);
80 |
81 | const actual = store.getState().get('test');
82 | const expected = initialState.merge({ reduced: 'yup' });
83 |
84 | expect(actual.toJS()).toEqual(expected.toJS());
85 | });
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/internals/templates/homePage.js:
--------------------------------------------------------------------------------
1 | /*
2 | * HomePage
3 | *
4 | * This is the first thing users see of our App, at the '/' route
5 | *
6 | * NOTE: while this component should technically be a stateless functional
7 | * component (SFC), hot reloading does not currently support SFCs. If hot
8 | * reloading is not a neccessity for you then you can refactor it and remove
9 | * the linting exception.
10 | */
11 |
12 | import React from 'react';
13 |
14 | export default class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function
15 |
16 | render() {
17 | return (
18 |
This is the Homepage!
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/internals/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | React.js Boilerplate
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/internals/templates/notFoundPage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NotFoundPage
3 | *
4 | * This is the page we show when the user visits a url that doesn't have a route
5 | *
6 | * NOTE: while this component should technically be a stateless functional
7 | * component (SFC), hot reloading does not currently support SFCs. If hot
8 | * reloading is not a neccessity for you then you can refactor it and remove
9 | * the linting exception.
10 | */
11 |
12 | import React from 'react';
13 |
14 | export default class NotFound extends React.Component { // eslint-disable-line react/prefer-stateless-function
15 |
16 | render() {
17 | return (
18 |
Page Not Found
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/internals/templates/reducers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Combine all reducers in this file and export the combined reducers.
3 | * If we were to do this in store.js, reducers wouldn't be hot reloadable.
4 | */
5 |
6 | import { combineReducers } from 'redux-immutable';
7 | import { fromJS } from 'immutable';
8 | import { LOCATION_CHANGE } from 'react-router-redux';
9 |
10 | /*
11 | * routeReducer
12 | *
13 | * The reducer merges route location changes into our immutable state.
14 | * The change is necessitated by moving to react-router-redux@4
15 | *
16 | */
17 |
18 | // Initial routing state
19 | const routeInitialState = fromJS({
20 | locationBeforeTransitions: null,
21 | });
22 |
23 | /**
24 | * Merge route into the global application state
25 | */
26 | function routeReducer(state = routeInitialState, action) {
27 | switch (action.type) {
28 | /* istanbul ignore next */
29 | case LOCATION_CHANGE:
30 | return state.merge({
31 | locationBeforeTransitions: action.payload,
32 | });
33 | default:
34 | return state;
35 | }
36 | }
37 |
38 | /**
39 | * Creates the main reducer with the asynchronously loaded ones
40 | */
41 | export default function createReducer(asyncReducers) {
42 | return combineReducers({
43 | route: routeReducer,
44 | ...asyncReducers,
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/internals/templates/routes.js:
--------------------------------------------------------------------------------
1 | // These are the pages you can go to.
2 | // They are all wrapped in the App component, which should contain the navbar etc
3 | // See http://blog.mxstbr.com/2016/01/react-apps-with-pages for more information
4 | // about the code splitting business
5 | // import { getAsyncInjectors } from 'utils/asyncInjectors';
6 |
7 | const errorLoading = (err) => {
8 | console.error('Dynamic page loading failed', err); // eslint-disable-line no-console
9 | };
10 |
11 | const loadModule = (cb) => (componentModule) => {
12 | cb(null, componentModule.default);
13 | };
14 |
15 | export default function createRoutes() {
16 | // Create reusable async injectors using getAsyncInjectors factory
17 | // const { injectReducer, injectSagas } = getAsyncInjectors(store);
18 |
19 | return [
20 | {
21 | path: '/',
22 | name: 'home',
23 | getComponent(nextState, cb) {
24 | const importModules = Promise.all([
25 | System.import('containers/HomePage'),
26 | ]);
27 |
28 | const renderRoute = loadModule(cb);
29 |
30 | importModules.then(([component]) => {
31 | renderRoute(component);
32 | });
33 |
34 | importModules.catch(errorLoading);
35 | },
36 | }, {
37 | path: '*',
38 | name: 'notfound',
39 | getComponent(nextState, cb) {
40 | System.import('containers/NotFoundPage')
41 | .then(loadModule(cb))
42 | .catch(errorLoading);
43 | },
44 | },
45 | ];
46 | }
47 |
--------------------------------------------------------------------------------
/internals/templates/selectors.js:
--------------------------------------------------------------------------------
1 | // selectLocationState expects a plain JS object for the routing state
2 | const selectLocationState = () => {
3 | let prevRoutingState;
4 | let prevRoutingStateJS;
5 |
6 | return (state) => {
7 | const routingState = state.get('route'); // or state.route
8 |
9 | if (!routingState.equals(prevRoutingState)) {
10 | prevRoutingState = routingState;
11 | prevRoutingStateJS = routingState.toJS();
12 | }
13 |
14 | return prevRoutingStateJS;
15 | };
16 | };
17 |
18 | export {
19 | selectLocationState,
20 | };
21 |
--------------------------------------------------------------------------------
/internals/templates/selectors.test.js:
--------------------------------------------------------------------------------
1 | import { fromJS } from 'immutable';
2 | import expect from 'expect';
3 |
4 | import { selectLocationState } from 'containers/App/selectors';
5 |
6 | describe('selectLocationState', () => {
7 | it('should select the route as a plain JS object', () => {
8 | const route = fromJS({
9 | locationBeforeTransitions: null,
10 | });
11 | const mockedState = fromJS({
12 | route,
13 | });
14 | expect(selectLocationState()(mockedState)).toEqual(route.toJS());
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/internals/templates/store.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create the store with asynchronously loaded reducers
3 | */
4 |
5 | import { createStore, applyMiddleware, compose } from 'redux';
6 | import { fromJS } from 'immutable';
7 | import { routerMiddleware } from 'react-router-redux';
8 | import createSagaMiddleware from 'redux-saga';
9 | import createReducer from './reducers';
10 |
11 | const sagaMiddleware = createSagaMiddleware();
12 | const devtools = window.devToolsExtension || (() => noop => noop);
13 |
14 | export default function configureStore(initialState = {}, history) {
15 | // Create the store with two middlewares
16 | // 1. sagaMiddleware: Makes redux-sagas work
17 | // 2. routerMiddleware: Syncs the location/URL path to the state
18 | const middlewares = [
19 | sagaMiddleware,
20 | routerMiddleware(history),
21 | ];
22 |
23 | const enhancers = [
24 | applyMiddleware(...middlewares),
25 | devtools(),
26 | ];
27 |
28 | const store = createStore(
29 | createReducer(),
30 | fromJS(initialState),
31 | compose(...enhancers)
32 | );
33 |
34 | // Create hook for async sagas
35 | store.runSaga = sagaMiddleware.run;
36 |
37 | // Make reducers hot reloadable, see http://mxs.is/googmo
38 | /* istanbul ignore next */
39 | if (module.hot) {
40 | System.import('./reducers').then((reducerModule) => {
41 | const createReducers = reducerModule.default;
42 | const nextReducers = createReducers(store.asyncReducers);
43 |
44 | store.replaceReducer(nextReducers);
45 | });
46 | }
47 |
48 | // Initialize it with no other reducers
49 | store.asyncReducers = {};
50 | return store;
51 | }
52 |
--------------------------------------------------------------------------------
/internals/templates/store.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test store addons
3 | */
4 |
5 | import expect from 'expect';
6 | import configureStore from './store';
7 | import { browserHistory } from 'react-router';
8 |
9 | describe('configureStore', () => {
10 | let store;
11 |
12 | before(() => {
13 | store = configureStore({}, browserHistory);
14 | });
15 |
16 | describe('asyncReducers', () => {
17 | it('should contain an object for async reducers', () => {
18 | expect(typeof store.asyncReducers).toEqual('object');
19 | });
20 | });
21 |
22 | describe('runSaga', () => {
23 | it('should contain a hook for `sagaMiddleware.run`', () => {
24 | expect(typeof store.runSaga).toEqual('function');
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/internals/templates/styles.css:
--------------------------------------------------------------------------------
1 | /**
2 | * styles.css
3 | *
4 | * App container styles
5 | */
6 |
7 | .container {
8 | display: block;
9 | }
10 |
--------------------------------------------------------------------------------
/internals/testing/karma.conf.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = require('../webpack/webpack.test.babel');
2 | const argv = require('minimist')(process.argv.slice(2));
3 | const path = require('path');
4 |
5 | module.exports = (config) => {
6 | config.set({
7 | frameworks: ['mocha'],
8 | reporters: ['coverage', 'mocha'],
9 | browsers: process.env.TRAVIS // eslint-disable-line no-nested-ternary
10 | ? ['ChromeTravis']
11 | : process.env.APPVEYOR
12 | ? ['IE'] : ['Chrome'],
13 |
14 | autoWatch: false,
15 | singleRun: true,
16 |
17 | client: {
18 | mocha: {
19 | grep: argv.grep,
20 | },
21 | },
22 |
23 | files: [
24 | {
25 | pattern: './test-bundler.js',
26 | watched: false,
27 | served: true,
28 | included: true,
29 | },
30 | ],
31 |
32 | preprocessors: {
33 | ['./test-bundler.js']: ['webpack', 'sourcemap'], // eslint-disable-line no-useless-computed-key
34 | },
35 |
36 | webpack: webpackConfig,
37 |
38 | // make Webpack bundle generation quiet
39 | webpackMiddleware: {
40 | noInfo: true,
41 | stats: 'errors-only',
42 | },
43 |
44 | customLaunchers: {
45 | ChromeTravis: {
46 | base: 'Chrome',
47 | flags: ['--no-sandbox'],
48 | },
49 | },
50 |
51 | coverageReporter: {
52 | dir: path.join(process.cwd(), 'coverage'),
53 | reporters: [
54 | { type: 'lcov', subdir: 'lcov' },
55 | { type: 'html', subdir: 'html' },
56 | { type: 'text-summary' },
57 | ],
58 | },
59 |
60 | });
61 | };
62 |
--------------------------------------------------------------------------------
/internals/testing/test-bundler.js:
--------------------------------------------------------------------------------
1 | // needed for regenerator-runtime
2 | // (ES7 generator support is required by redux-saga)
3 | import 'babel-polyfill';
4 |
5 | // If we need to use Chai, we'll have already chaiEnzyme loaded
6 | import chai from 'chai';
7 | import chaiEnzyme from 'chai-enzyme';
8 | chai.use(chaiEnzyme());
9 |
10 | // Include all .js files under `app`, except app.js, reducers.js, routes.js and
11 | // store.js. This is for isparta code coverage
12 | const context = require.context('../../app', true, /^^((?!(app|reducers|routes|store)).)*\.js$/);
13 | context.keys().forEach(context);
14 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.dll.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WEBPACK DLL GENERATOR
3 | *
4 | * This profile is used to cache webpack's module
5 | * contexts for external library and framework type
6 | * dependencies which will usually not change often enough
7 | * to warrant building them from scratch every time we use
8 | * the webpack process.
9 | */
10 |
11 | const { join } = require('path');
12 | const defaults = require('lodash/defaultsDeep');
13 | const webpack = require('webpack');
14 | const pkg = require(join(process.cwd(), 'package.json'));
15 | const dllPlugin = require('../config').dllPlugin;
16 |
17 | if (!pkg.dllPlugin) { process.exit(0); }
18 |
19 | const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
20 | const outputPath = join(process.cwd(), dllConfig.path);
21 |
22 | module.exports = {
23 | context: process.cwd(),
24 | entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
25 | devtool: 'eval',
26 | output: {
27 | filename: '[name].dll.js',
28 | path: outputPath,
29 | library: '[name]',
30 | },
31 | plugins: [
32 | new webpack.DllPlugin({ name: '[name]', path: join(outputPath, '[name].json') }), // eslint-disable-line no-new
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | /* eslint consistent-return:0 */
2 |
3 | const express = require('express');
4 | const logger = require('./logger');
5 |
6 | const argv = require('minimist')(process.argv.slice(2));
7 | const setup = require('./middlewares/frontendMiddleware');
8 | const isDev = process.env.NODE_ENV !== 'production';
9 | const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
10 | const resolve = require('path').resolve;
11 | const app = express();
12 |
13 | // If you need a backend, e.g. an API, add your custom backend-specific middleware here
14 | // app.use('/api', myApi);
15 |
16 | // In production we need to pass these values in instead of relying on webpack
17 | setup(app, {
18 | outputPath: resolve(process.cwd(), 'build'),
19 | publicPath: '/',
20 | });
21 |
22 | // get the intended port number, use port 3000 if not provided
23 | const port = argv.port || process.env.PORT || 3000;
24 |
25 | // Start your app.
26 | app.listen(port, (err) => {
27 | if (err) {
28 | return logger.error(err.message);
29 | }
30 |
31 | // Connect to ngrok in dev mode
32 | if (ngrok) {
33 | ngrok.connect(port, (innerErr, url) => {
34 | if (innerErr) {
35 | return logger.error(innerErr);
36 | }
37 |
38 | logger.appStarted(port, url);
39 | });
40 | } else {
41 | logger.appStarted(port);
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/server/logger.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 | const ip = require('ip');
5 |
6 | const divider = chalk.gray('\n-----------------------------------');
7 |
8 | /**
9 | * Logger middleware, you can customize it to make messages more personal
10 | */
11 | const logger = {
12 |
13 | // Called whenever there's an error on the server we want to print
14 | error: err => {
15 | console.error(chalk.red(err));
16 | },
17 |
18 | // Called when express.js app starts on given port w/o errors
19 | appStarted: (port, tunnelStarted) => {
20 | console.log(`Server started ${chalk.green('✓')}`);
21 |
22 | // If the tunnel started, log that and the URL it's available at
23 | if (tunnelStarted) {
24 | console.log(`Tunnel initialised ${chalk.green('✓')}`);
25 | }
26 |
27 | console.log(`
28 | ${chalk.bold('Access URLs:')}${divider}
29 | Localhost: ${chalk.magenta(`http://localhost:${port}`)}
30 | LAN: ${chalk.magenta(`http://${ip.address()}:${port}`) +
31 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}${divider}
32 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
33 | `);
34 | },
35 | };
36 |
37 | module.exports = logger;
38 |
--------------------------------------------------------------------------------
/server/middlewares/frontendMiddleware.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | const express = require('express');
3 | const path = require('path');
4 | const compression = require('compression');
5 | const pkg = require(path.resolve(process.cwd(), 'package.json'));
6 |
7 | // Dev middleware
8 | const addDevMiddlewares = (app, webpackConfig) => {
9 | const webpack = require('webpack');
10 | const webpackDevMiddleware = require('webpack-dev-middleware');
11 | const webpackHotMiddleware = require('webpack-hot-middleware');
12 | const compiler = webpack(webpackConfig);
13 | const middleware = webpackDevMiddleware(compiler, {
14 | noInfo: true,
15 | publicPath: webpackConfig.output.publicPath,
16 | silent: true,
17 | stats: 'errors-only',
18 | });
19 |
20 | app.use(middleware);
21 | app.use(webpackHotMiddleware(compiler));
22 |
23 | // Since webpackDevMiddleware uses memory-fs internally to store build
24 | // artifacts, we use it instead
25 | const fs = middleware.fileSystem;
26 |
27 | if (pkg.dllPlugin) {
28 | app.get(/\.dll\.js$/, (req, res) => {
29 | const filename = req.path.replace(/^\//, '');
30 | res.sendFile(path.join(process.cwd(), pkg.dllPlugin.path, filename));
31 | });
32 | }
33 |
34 | app.get('*', (req, res) => {
35 | fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => {
36 | if (err) {
37 | res.sendStatus(404);
38 | } else {
39 | res.send(file.toString());
40 | }
41 | });
42 | });
43 | };
44 |
45 | // Production middlewares
46 | const addProdMiddlewares = (app, options) => {
47 | const publicPath = options.publicPath || '/';
48 | const outputPath = options.outputPath || path.resolve(process.cwd(), 'build');
49 |
50 | // compression middleware compresses your server responses which makes them
51 | // smaller (applies also to assets). You can read more about that technique
52 | // and other good practices on official Express.js docs http://mxs.is/googmy
53 | app.use(compression());
54 | app.use(publicPath, express.static(outputPath));
55 |
56 | app.get('*', (req, res) => res.sendFile(path.resolve(outputPath, 'index.html')));
57 | };
58 |
59 | /**
60 | * Front-end middleware
61 | */
62 | module.exports = (app, options) => {
63 | const isProd = process.env.NODE_ENV === 'production';
64 |
65 | if (isProd) {
66 | addProdMiddlewares(app, options);
67 | } else {
68 | const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
69 | addDevMiddlewares(app, webpackConfig);
70 | }
71 |
72 | return app;
73 | };
74 |
--------------------------------------------------------------------------------