Skip to content

Instantly share code, notes, and snippets.

@danalloway
Last active November 7, 2017 08:07
Show Gist options
  • Select an option

  • Save danalloway/dc8b87647b7c64ed9746cd632f95f53a to your computer and use it in GitHub Desktop.

Select an option

Save danalloway/dc8b87647b7c64ed9746cd632f95f53a to your computer and use it in GitHub Desktop.

Revisions

  1. danalloway revised this gist Nov 6, 2017. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions app.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    import { h } from 'preact';
    import Link from 'react-router-dom/Link';
    import Route from 'react-router-dom/Route';
    import Switch from 'react-router-dom/Switch';

    import Home from '../routes/Home';
    import About from '../routes/About';

    const App = () => (
    <div>
    <Link to="/">Home</Link>
    <Link to="/about">About</Link>

    <Switch>
    <Route exact path="/" component={Home} />
    <Route path="/about" component={About} />
    </Switch>
    </div>
    );

    export default App;
  2. danalloway revised this gist Nov 6, 2017. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -18,11 +18,11 @@ const app = require('express')();

    // Our application (note the three ssr-* bundles we're using here)
    const dir = resolve(__dirname, 'build/ssr-build');
    const bundle = require(dir + '/ssr-bundle');
    const createHistory = require(dir + '/ssr-history');
    const configureStore = require(dir + '/ssr-store');
    const bundle = require(dir + '/ssr-bundle').default;
    const createHistory = require(dir + '/ssr-history').default;
    const configureStore = require(dir + '/ssr-store').default;

    const App = bundle.default;
    const App = bundle;
    const RGX = /<div id="app"[^>]*>.*?(?=<script)/i;

    const template = fs.readFileSync(resolve(__dirname, 'build/index.html'), 'utf8');
  3. danalloway revised this gist Nov 6, 2017. 1 changed file with 74 additions and 0 deletions.
    74 changes: 74 additions & 0 deletions firebase.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    {
    "hosting": {
    "public": "build",
    "rewrites": [
    {
    "source": "**",
    "function": "app"
    }
    ],
    "headers": [
    {
    "source": "**",
    "headers": [
    {
    "key": "Cache-Control",
    "value": "public, max-age=3600, no-cache"
    },
    {
    "key": "Access-Control-Max-Age",
    "value": "600"
    }
    ]
    },
    {
    "source": "/sw.js",
    "headers": [
    {
    "key": "Cache-Control",
    "value": "private, no-cache"
    }
    ]
    },
    {
    "source": "**/*.chunk.*.js",
    "headers": [
    {
    "key": "Cache-Control",
    "value": "public, max-age=31536000"
    }
    ]
    },
    {
    "source": "/",
    "headers": [
    {
    "key": "Link",
    "value": "</bundle.16517.js>; rel=preload; as=script, </style.aab19.css>; rel=preload; as=style"
    }
    ]
    },
    {
    "source": "/Home",
    "headers": [
    {
    "key": "Link",
    "value": "</bundle.16517.js>; rel=preload; as=script, </style.aab19.css>; rel=preload; as=style, </route-Home.chunk.3bba7.js>; rel=preload; as=script"
    }
    ]
    },
    {
    "source": "/About",
    "headers": [
    {
    "key": "Link",
    "value": "</bundle.16517.js>; rel=preload; as=script, </style.aab19.css>; rel=preload; as=style, </route-About.chunk.ddd48.js>; rel=preload; as=script"
    }
    ]
    }
    ]
    },
    "functions": {
    "source": "functions"
    }
    }
  4. danalloway revised this gist Nov 6, 2017. 2 changed files with 40 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions history.js
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,9 @@
    // static (server) version of the app must use the in-memory history

    const createBrowserHistory = require('history').createBrowserHistory;
    const createMemoryHistory = require('history').createMemoryHistory;
    const createBrowserHistory =import createBrowserHistory from 'history/createBrowserHistory';
    import createMemoryHistory from 'history/createMemoryHistory';

    module.exports = opts =>
    export default opts =>
    typeof window === 'undefined'
    ? createMemoryHistory(opts)
    : createBrowserHistory(opts);
    37 changes: 37 additions & 0 deletions store.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    import { createStore, applyMiddleware, combineReducers } from 'redux';
    import { routerMiddleware, routerReducer } from 'react-router-redux';
    import { createLogger } from 'redux-logger';
    import { persistReducer } from 'redux-persist';

    import appReducer from './modules';
    import persistConfig from './persistConfig';

    export default history => {
    let reducers = combineReducers({
    ...appReducer,
    router: routerReducer
    });
    let middleware = [routerMiddleware(history)];
    let preloadedState;

    if (typeof window !== 'undefined') {
    reducers = persistReducer(persistConfig, reducers);
    middleware = [
    ...middleware,
    createLogger({
    collapsed: true,
    duration: true
    })
    ];
    preloadedState = window.__PRELOADED_STATE__;
    delete window.__PRELOADED_STATE__;
    }

    const store = createStore(
    reducers,
    preloadedState,
    applyMiddleware(...middleware)
    );

    return store;
    };
  5. danalloway revised this gist Nov 6, 2017. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,10 @@ const template = fs.readFileSync(resolve(__dirname, 'build/index.html'), 'utf8')

    function renderApplication(req, res) {
    const url = req.url;

    // this will feed the current URL from Express to the in-memory store that Redux and the Router will use
    const history = createHistory({ initialEntries: [url] });

    const store = configureStore(history);
    const body = render(h(App, { history, store, url }));
    const preloadedState = JSON.stringify(store.getState()).replace(/</g, '\\u003c');
  6. danalloway revised this gist Nov 6, 2017. 1 changed file with 9 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions history.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    // static (server) version of the app must use the in-memory history

    const createBrowserHistory = require('history').createBrowserHistory;
    const createMemoryHistory = require('history').createMemoryHistory;

    module.exports = opts =>
    typeof window === 'undefined'
    ? createMemoryHistory(opts)
    : createBrowserHistory(opts);
  7. danalloway revised this gist Nov 6, 2017. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions static.js
    Original file line number Diff line number Diff line change
    @@ -6,6 +6,9 @@ import createHistory from './state/history';
    import configureStore from './state/store';
    import App from './components/app';

    // we have to provide a default value for `history` and `store` here
    // as `preact build` will attempt it's on pass at SSRing our app
    // and it wont't have either of those things to use, and fail
    const Static = ({ history = createHistory(), store, url = '/' }) => (
    <Provider store={store || configureStore(history)}>
    <ConnectedRouter location={url} history={history}>
  8. danalloway revised this gist Nov 6, 2017. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -2,21 +2,21 @@
    * I'm using Firebase Functions to SSR my application.
    */

    # Firebase
    // Firebase
    const functions = require('firebase-functions');

    # Node
    // Node
    const fs = require('fs');
    const { resolve } = require('path');

    # Preact
    // Preact
    const { h } = require('preact');
    const render = require('preact-render-to-string');

    # Express Server
    // Express Server
    const app = require('express')();

    # Our application (note the three ssr-* bundles we're using here)
    // Our application (note the three ssr-* bundles we're using here)
    const dir = resolve(__dirname, 'build/ssr-build');
    const bundle = require(dir + '/ssr-bundle');
    const createHistory = require(dir + '/ssr-history');
  9. danalloway created this gist Nov 6, 2017.
    26 changes: 26 additions & 0 deletions client.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    import { h } from 'preact';
    import { Provider } from 'preact-redux';
    import { ConnectedRouter } from 'react-router-redux';
    import { persistStore } from 'redux-persist';

    import PersistGate from './components/PersistGate';
    import App from './components/app';

    import configureStore from './state/store';
    import createHistory from './state/history';

    const history = createHistory();
    const store = configureStore(history);
    const persistor = persistStore(store);

    const Client = () => (
    <Provider store={store}>
    <PersistGate persistor={persistor}>
    <ConnectedRouter history={history}>
    <App />
    </ConnectedRouter>
    </PersistGate>
    </Provider>
    );

    export default Client;
    23 changes: 23 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    /**
    * This is the main entry point of my Preact application.
    * We will need to render Static (server) and Client versions.
    */

    import { h } from 'preact';

    import Client from './client';
    import Static from './static';

    import './style';

    const Root = ({ history, store, url }) => (
    <div id="app">
    {typeof window === 'undefined' ? (
    <Static history={history} store={store} url={url} />
    ) : (
    <Client />
    )}
    </div>
    );

    export default Root;
    20 changes: 20 additions & 0 deletions preact.config.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    /**
    * Our SSR code will need to access a compiled version of our application.
    * The three entries below (the ssr-bundle is already provided by preact-cli)
    * will provide the parts our SSR code will need to require later.
    */

    export default function(config, env, helpers) {
    /**
    * We need to access our Redux store / history code on the server.
    * so generate transpiled bundles so we can easily `require` them later
    */
    if (env.ssr) {
    config.entry = {
    'ssr-bundle': env.source('index.js'),
    'ssr-history': env.source('state/history.js'),
    'ssr-store': env.source('state/store.js')
    };
    config.output.filename = '[name].js';
    }
    }
    43 changes: 43 additions & 0 deletions server.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    /**
    * I'm using Firebase Functions to SSR my application.
    */

    # Firebase
    const functions = require('firebase-functions');

    # Node
    const fs = require('fs');
    const { resolve } = require('path');

    # Preact
    const { h } = require('preact');
    const render = require('preact-render-to-string');

    # Express Server
    const app = require('express')();

    # Our application (note the three ssr-* bundles we're using here)
    const dir = resolve(__dirname, 'build/ssr-build');
    const bundle = require(dir + '/ssr-bundle');
    const createHistory = require(dir + '/ssr-history');
    const configureStore = require(dir + '/ssr-store');

    const App = bundle.default;
    const RGX = /<div id="app"[^>]*>.*?(?=<script)/i;

    const template = fs.readFileSync(resolve(__dirname, 'build/index.html'), 'utf8');

    function renderApplication(req, res) {
    const url = req.url;
    const history = createHistory({ initialEntries: [url] });
    const store = configureStore(history);
    const body = render(h(App, { history, store, url }));
    const preloadedState = JSON.stringify(store.getState()).replace(/</g, '\\u003c');
    const html = template.replace(RGX, `${body}<script>window.__PRELOADED_STATE__ = ${preloadedState}</script>`);

    res.send(html);
    }

    app.get('*', (req, res) => renderApplication(req, res));

    exports.app = functions.https.onRequest(app);
    17 changes: 17 additions & 0 deletions static.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    import { h } from 'preact';
    import { Provider } from 'preact-redux';
    import { ConnectedRouter } from 'react-router-redux';

    import createHistory from './state/history';
    import configureStore from './state/store';
    import App from './components/app';

    const Static = ({ history = createHistory(), store, url = '/' }) => (
    <Provider store={store || configureStore(history)}>
    <ConnectedRouter location={url} history={history}>
    <App />
    </ConnectedRouter>
    </Provider>
    );

    export default Static;