├── .all-contributorsrc ├── .babelrc ├── .editorconfig ├── .env_example ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .modernizrrc ├── .npmrc ├── .nvmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── client ├── components │ └── ReactHotLoader.js ├── index.js ├── polyfills │ └── index.js └── registerServiceWorker.js ├── config ├── components │ └── ClientConfig.js ├── index.js ├── utils │ └── envVars.js └── values.js ├── internal ├── .eslintrc ├── development │ ├── createVendorDLL.js │ ├── hotClientServer.js │ ├── hotDevelopment.js │ ├── hotNodeServer.js │ ├── index.js │ └── listenerManager.js ├── docs │ ├── ADDING_AN_API_BUNDLE.md │ ├── DEPLOY_TO_NOW.md │ ├── FAQ.md │ ├── FEATURE_BRANCHES.md │ ├── PKG_SCRIPTS.md │ ├── PROJECT_CONFIG.md │ └── PROJECT_OVERVIEW.md ├── jest │ ├── assetMock.js │ └── styleMock.js ├── scripts │ ├── analyze.js │ ├── build.js │ ├── clean.js │ ├── deploy.js │ └── preinstall.js ├── utils.js └── webpack │ ├── configFactory.js │ └── withServiceWorker │ ├── index.js │ └── offlinePageTemplate.js ├── package-lock.json ├── package.json ├── public ├── browserconfig.xml ├── favicon.ico ├── favicons │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── favicon-128.png │ ├── favicon-16x16.png │ ├── favicon-196x196.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ └── safari-pinned-tab.svg ├── manifest.json └── robots.txt ├── server ├── index.js └── middleware │ ├── clientBundle.js │ ├── errorHandlers.js │ ├── offlinePage.js │ ├── reactApplication │ ├── ServerHTML.js │ ├── getClientBundleEntryAssets.js │ └── index.js │ ├── security.js │ └── serviceWorker.js └── shared ├── README.md ├── components ├── DemoApp │ ├── AsyncAboutRoute │ │ ├── AboutRoute.js │ │ ├── __tests__ │ │ │ ├── AboutRoute.test.js │ │ │ └── __snapshots__ │ │ │ │ └── AboutRoute.test.js.snap │ │ └── index.js │ ├── AsyncCounterRoute │ │ ├── CounterRoute.js │ │ └── index.js │ ├── AsyncHomeRoute │ │ ├── HomeRoute.js │ │ ├── __tests__ │ │ │ ├── HomeRoute.test.js │ │ │ └── __snapshots__ │ │ │ │ └── HomeRoute.test.js.snap │ │ └── index.js │ ├── Error404 │ │ ├── __tests__ │ │ │ ├── Error404.test.js │ │ │ └── __snapshots__ │ │ │ │ └── Error404.test.js.snap │ │ └── index.js │ ├── Header │ │ ├── Logo │ │ │ ├── index.js │ │ │ └── logo.png │ │ ├── Menu │ │ │ ├── __tests__ │ │ │ │ ├── Menu.test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Menu.test.js.snap │ │ │ └── index.js │ │ └── index.js │ ├── globals.css │ └── index.js └── HTML │ └── index.js └── utils ├── arrays ├── __tests__ │ └── removeNil.test.js ├── index.js └── removeNil.js ├── logic ├── __tests__ │ └── ifElse.test.js ├── ifElse.js └── index.js └── objects ├── __tests__ ├── filterWithRules.test.js └── mergeDeep.test.js ├── filterWithRules.js ├── index.js └── mergeDeep.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "react-universally", 3 | "projectOwner": "ctrlplusb", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": true, 9 | "contributors": [ 10 | { 11 | "login": "aoc", 12 | "name": "Andrés Calabrese", 13 | "avatar_url": "https://avatars3.githubusercontent.com/u/243161?v=3", 14 | "profile": "https://github.com/aoc", 15 | "contributions": [ 16 | "code" 17 | ] 18 | }, 19 | { 20 | "login": "andreyluiz", 21 | "name": "Andrey Luiz", 22 | "avatar_url": "https://avatars3.githubusercontent.com/u/1965897?v=3", 23 | "profile": "https://andreyluiz.github.io/", 24 | "contributions": [ 25 | "code" 26 | ] 27 | }, 28 | { 29 | "login": "alinporumb", 30 | "name": "Alin Porumb", 31 | "avatar_url": "https://avatars3.githubusercontent.com/u/3148205?v=3", 32 | "profile": "https://github.com/alinporumb", 33 | "contributions": [ 34 | "code" 35 | ] 36 | }, 37 | { 38 | "login": "bkniffler", 39 | "name": "Benjamin Kniffler", 40 | "avatar_url": "https://avatars0.githubusercontent.com/u/4349324?v=3", 41 | "profile": "https://github.com/bkniffler", 42 | "contributions": [ 43 | "code" 44 | ] 45 | }, 46 | { 47 | "login": "birkir", 48 | "name": "Birkir Rafn Guðjónsson", 49 | "avatar_url": "https://avatars0.githubusercontent.com/u/180773?v=3", 50 | "profile": "https://medium.com/@birkir.gudjonsson", 51 | "contributions": [ 52 | "question", 53 | "bug", 54 | "code", 55 | "review" 56 | ] 57 | }, 58 | { 59 | "login": "carsonperrotti", 60 | "name": "Carson Perrotti", 61 | "avatar_url": "https://avatars0.githubusercontent.com/u/2063102?v=3", 62 | "profile": "http://carsonperrotti.com", 63 | "contributions": [ 64 | "question", 65 | "code", 66 | "doc", 67 | "review" 68 | ] 69 | }, 70 | { 71 | "login": "LorbusChris", 72 | "name": "Christian Glombek", 73 | "avatar_url": "https://avatars1.githubusercontent.com/u/13365531?v=3", 74 | "profile": "https://github.com/LorbusChris", 75 | "contributions": [ 76 | "bug", 77 | "code" 78 | ] 79 | }, 80 | { 81 | "login": "codepunkt", 82 | "name": "Christoph Werner", 83 | "avatar_url": "https://avatars3.githubusercontent.com/u/603683?v=3", 84 | "profile": "https://twitter.com/code_punkt", 85 | "contributions": [ 86 | "question", 87 | "bug", 88 | "code", 89 | "review" 90 | ] 91 | }, 92 | { 93 | "login": "threehams", 94 | "name": "David Edmondson", 95 | "avatar_url": "https://avatars0.githubusercontent.com/u/1399894?v=3", 96 | "profile": "https://github.com/threehams", 97 | "contributions": [ 98 | "code" 99 | ] 100 | }, 101 | { 102 | "login": "diondirza", 103 | "name": "Dion Dirza", 104 | "avatar_url": "https://avatars0.githubusercontent.com/u/10954870?v=3", 105 | "profile": "https://github.com/diondirza", 106 | "contributions": [ 107 | "question", 108 | "bug", 109 | "code", 110 | "doc", 111 | "review" 112 | ] 113 | }, 114 | { 115 | "login": "evgenyboxer", 116 | "name": "Evgeny Boxer", 117 | "avatar_url": "https://avatars0.githubusercontent.com/u/254095?v=3", 118 | "profile": "https://github.com/evgenyboxer", 119 | "contributions": [ 120 | "bug", 121 | "code" 122 | ] 123 | }, 124 | { 125 | "login": "kohlmannj", 126 | "name": "Joe Kohlmann", 127 | "avatar_url": "https://avatars2.githubusercontent.com/u/191304?v=3", 128 | "profile": "http://kohlmannj.com", 129 | "contributions": [ 130 | "bug", 131 | "code" 132 | ] 133 | }, 134 | { 135 | "login": "lucianlature", 136 | "name": "Lucian Lature", 137 | "avatar_url": "https://avatars2.githubusercontent.com/u/24992?v=3", 138 | "profile": "https://www.linkedin.com/in/lucianlature/", 139 | "contributions": [ 140 | "bug", 141 | "code", 142 | "review" 143 | ] 144 | }, 145 | { 146 | "login": "markshlick", 147 | "name": "Mark Shlick", 148 | "avatar_url": "https://avatars1.githubusercontent.com/u/1624703?v=3", 149 | "profile": "https://github.com/markshlick", 150 | "contributions": [ 151 | "code" 152 | ] 153 | }, 154 | { 155 | "login": "rlindskog", 156 | "name": "Ryan Lindskog", 157 | "avatar_url": "https://avatars1.githubusercontent.com/u/7436773?v=3", 158 | "profile": "https://www.RyanLindskog.com/", 159 | "contributions": [ 160 | "code" 161 | ] 162 | }, 163 | { 164 | "login": "enten", 165 | "name": "Steven Enten", 166 | "avatar_url": "https://avatars1.githubusercontent.com/u/977713?v=3", 167 | "profile": "http://enten.fr", 168 | "contributions": [ 169 | "question", 170 | "bug", 171 | "code", 172 | "review" 173 | ] 174 | }, 175 | { 176 | "login": "ctrlplusb", 177 | "name": "Sean Matheson", 178 | "avatar_url": "https://avatars1.githubusercontent.com/u/12164768?v=3", 179 | "profile": "http://www.ctrlplusb.com", 180 | "contributions": [ 181 | "question", 182 | "bug", 183 | "code", 184 | "doc", 185 | "example", 186 | "review", 187 | "test", 188 | "tool" 189 | ] 190 | }, 191 | { 192 | "login": "strues", 193 | "name": "Steven Truesdell", 194 | "avatar_url": "https://avatars0.githubusercontent.com/u/6218853?v=3", 195 | "profile": "https://steventruesdell.com", 196 | "contributions": [ 197 | "question", 198 | "bug", 199 | "code", 200 | "doc", 201 | "test" 202 | ] 203 | }, 204 | { 205 | "login": "datoml", 206 | "name": "Thomas Leitgeb", 207 | "avatar_url": "https://avatars0.githubusercontent.com/u/10552487?v=3", 208 | "profile": "https://twitter.com/_datoml", 209 | "contributions": [ 210 | "bug", 211 | "code" 212 | ] 213 | }, 214 | { 215 | "login": "tsnieman", 216 | "name": "Tyler Nieman", 217 | "avatar_url": "https://avatars0.githubusercontent.com/u/595711?v=3", 218 | "profile": "http://tsnieman.net/", 219 | "contributions": [ 220 | "code" 221 | ] 222 | } 223 | ] 224 | } 225 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | // We use the .babelrc here to help transpile our tools and support tooling 2 | // (such as testing libs). 3 | // I would recommend that you keep this file in feature parity with 4 | // the config/babelConfigResolver. That way you get to use the same level of 5 | // javascript syntax across the entire project. 6 | // It's nice not having to worry/think about what level of javascript syntax 7 | // is supported for each context. 8 | { 9 | "presets": [ 10 | ["env", { "targets": { "node": true } }], 11 | "stage-3", 12 | "react" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://editorconfig.org/ 2 | # EditorConfig helps developers define and maintain consistent coding styles 3 | # between different editors and IDEs. The EditorConfig project consists of a 4 | # file format for defining coding styles and a collection of text editor plugins 5 | # that enable editors to read the file format and adhere to defined styles. 6 | # EditorConfig files are easily readable and they work nicely with version 7 | # control systems. 8 | 9 | root = true 10 | 11 | [*] 12 | indent_style = space 13 | indent_size = 2 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.env_example: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # This is an example environment configuration file. You can create your own 3 | # .env implementation or pass them via the CLI. 4 | # 5 | # You could also decide to supply some of your environment variables from a .env 6 | # file, and others via the CLI. This allows you to easily store "safe" 7 | # environment variables within this file, and then manage your more sensitive 8 | # environment variables seperately. 9 | # ============================================================================== 10 | 11 | # The host on which to run the server. 12 | HOST=localhost 13 | 14 | # The port on which to run the server. 15 | PORT=1337 16 | 17 | # The port on which to run the client bundle dev server (i.e. used during 18 | # development only). 19 | CLIENT_DEV_PORT=7331 20 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "env": { 5 | "browser": true, 6 | "es6": true, 7 | "node": true, 8 | "jest": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 6, 12 | "sourceType": "module", 13 | "ecmaFeatures": { 14 | "defaultParams": true 15 | } 16 | }, 17 | "rules": { 18 | // A jsx extension is not required for files containing jsx 19 | "react/jsx-filename-extension": 0, 20 | // This rule struggles with flow and class properties 21 | "react/sort-comp": 0, 22 | // ignore linebreak style. the CRLF / LF endings wont matter 23 | // if a windows user correctly converts CRLF to LF upon commits otherwise 24 | // there are errors every line. 25 | "linebreak-style": 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Environment Configuration 2 | .env 3 | 4 | # Build output folders 5 | build/ 6 | 7 | # Logs 8 | logs 9 | *.log 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Dependency directory 23 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 24 | node_modules 25 | 26 | # Debug log from npm 27 | npm-debug.log 28 | 29 | # IntelliJ IDE ignore 30 | .idea 31 | 32 | # Visual Studio Code 33 | .vscode 34 | .history 35 | 36 | # flow-typed Lib Defs 37 | flow-typed/ 38 | 39 | # Flow Coverage Report 40 | flow-coverage/ 41 | 42 | # Happypack 43 | .happypack 44 | 45 | # OSX Files 46 | .DS_Store 47 | -------------------------------------------------------------------------------- /.modernizrrc: -------------------------------------------------------------------------------- 1 | { 2 | "minify": true, 3 | "options": [], 4 | "feature-detects": [ 5 | "elem/picture" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact = true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v6.11.1 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Sean Matheson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

React, Universally

3 |

4 |

A starter kit for universal react applications.

5 |

6 | 7 | [![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors) 8 | 9 | ## About 10 | 11 | This starter kit contains all the build tooling and configuration you need to kick off your next universal React project, whilst containing a minimal "project" set up allowing you to make your own architecture decisions (Redux/MobX etc). 12 | 13 | > NOTICE: Please read this important [issue](https://github.com/ctrlplusb/react-universally/issues/409) about the behaviour of this project when using `react-async-component`, which is by default bundled with it. 14 | 15 | ## Features 16 | 17 | - 👀 `react` as the view. 18 | - 🔀 `react-router` v4 as the router. 19 | - 🚄 `express` server. 20 | - 🎭 `jest` as the test framework. 21 | - 💄 Combines `prettier` and Airbnb's ESlint configuration - performing code formatting on commit. Stop worrying about code style consistency. 22 | - 🖌 Very basic CSS support - it's up to you to extend it with CSS Modules etc. 23 | - ✂️ Code splitting - easily define code split points in your source using `react-async-component`. 24 | - 🌍 Server Side Rendering. 25 | - 😎 Progressive Web Application ready, with offline support, via a Service Worker. 26 | - 🐘 Long term browser caching of assets with automated cache invalidation. 27 | - 📦 All source is bundled using Webpack v3. 28 | - 🚀 Full ES2017+ support - use the exact same JS syntax across the entire project. No more folder context switching! We also only use syntax that is stage-3 or later in the TC39 process. 29 | - 🔧 Centralised application configuration with helpers to avoid boilerplate in your code. Also has support for environment specific configuration files. 30 | - 🔥 Extreme live development - hot reloading of ALL changes to client/server source, with auto development server restarts when your application configuration changes. All this with a high level of error tolerance and verbose logging to the console. 31 | - ⛑ SEO friendly - `react-helmet` provides control of the page title/meta/styles/scripts from within your components. 32 | - 🤖 Optimised Webpack builds via HappyPack and an auto generated Vendor DLL for smooth development experiences. 33 | - 🍃 Tree-shaking, courtesy of Webpack. 34 | - 👮 Security on the `express` server using `helmet` and `hpp`. 35 | - 🏜 Asset bundling support. e.g. images/fonts. 36 | - 🎛 Preconfigured to support development and optimised production builds. 37 | - ❤️ Preconfigured to deploy to `now` with a single command. 38 | 39 | Redux/MobX, data persistence, modern styling frameworks and all the other bells and whistles have been explicitly excluded from this starter kit. It's up to you to decide what technologies you would like to add to your own implementation based upon your own needs. 40 | 41 | > However, we now include a set of "feature branches", each implementing a technology on top of the clean master branch. This provides you with an example on how to integrate said technologies, or use the branches to merge in a configuration that meets your requirements. See the [`Feature Branches`](/internal/docs/FEATURE_BRANCHES.md) documentation for more. 42 | 43 | ## Getting started 44 | 45 | ```bash 46 | git clone https://github.com/ctrlplusb/react-universally my-project 47 | cd my-project 48 | npm install 49 | npm run develop 50 | ``` 51 | 52 | Now go make some changes to the `Home` component to see the tooling in action. 53 | 54 | ## Docs 55 | 56 | - [Project Overview](/internal/docs/PROJECT_OVERVIEW.md) 57 | - [Project Configuration](/internal/docs/PROJECT_CONFIG.md) 58 | - [Package Script Commands](/internal/docs/PKG_SCRIPTS.md) 59 | - [FAQ](/internal/docs/FAQ.md) 60 | - [Feature Branches](/internal/docs/FEATURE_BRANCHES.md) 61 | - [Deploy your very own Server Side Rendering React App in 5 easy steps](/internal/docs/DEPLOY_TO_NOW.md) 62 | - [Changelog](/CHANGELOG.md) 63 | 64 | ## Who's using it and where? 65 | 66 | You can see who is using it and how in [the comments here](https://github.com/ctrlplusb/react-universally/issues/437). Feel free to add to that telling us how you are using it, we'd love to hear from you. 67 | 68 | ## Contributors 69 | 70 | Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): 71 | 72 | 73 | | [
Andrés Calabrese](https://github.com/aoc)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=aoc) | [
Andrey Luiz](https://andreyluiz.github.io/)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=andreyluiz) | [
Alin Porumb](https://github.com/alinporumb)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=alinporumb) | [
Benjamin Kniffler](https://github.com/bkniffler)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=bkniffler) | [
Birkir Rafn Guðjónsson](https://medium.com/@birkir.gudjonsson)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Abirkir) [💻](https://github.com/ctrlplusb/react-universally/commits?author=birkir) 👀 | [
Carson Perrotti](http://carsonperrotti.com)
💬 [💻](https://github.com/ctrlplusb/react-universally/commits?author=carsonperrotti) [📖](https://github.com/ctrlplusb/react-universally/commits?author=carsonperrotti) 👀 | [
Christian Glombek](https://github.com/LorbusChris)
[🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3ALorbusChris) [💻](https://github.com/ctrlplusb/react-universally/commits?author=LorbusChris) | 74 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 75 | | [
Christoph Werner](https://twitter.com/code_punkt)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Acodepunkt) [💻](https://github.com/ctrlplusb/react-universally/commits?author=codepunkt) 👀 | [
David Edmondson](https://github.com/threehams)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=threehams) | [
Dion Dirza](https://github.com/diondirza)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Adiondirza) [💻](https://github.com/ctrlplusb/react-universally/commits?author=diondirza) [📖](https://github.com/ctrlplusb/react-universally/commits?author=diondirza) 👀 | [
Evgeny Boxer](https://github.com/evgenyboxer)
[🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Aevgenyboxer) [💻](https://github.com/ctrlplusb/react-universally/commits?author=evgenyboxer) | [
Joe Kohlmann](http://kohlmannj.com)
[🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Akohlmannj) [💻](https://github.com/ctrlplusb/react-universally/commits?author=kohlmannj) | [
Lucian Lature](https://www.linkedin.com/in/lucianlature/)
[🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Alucianlature) [💻](https://github.com/ctrlplusb/react-universally/commits?author=lucianlature) 👀 | [
Mark Shlick](https://github.com/markshlick)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=markshlick) | 76 | | [
Ryan Lindskog](https://www.RyanLindskog.com/)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=rlindskog) | [
Steven Enten](http://enten.fr)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Aenten) [💻](https://github.com/ctrlplusb/react-universally/commits?author=enten) 👀 | [
Sean Matheson](http://www.ctrlplusb.com)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Actrlplusb) [💻](https://github.com/ctrlplusb/react-universally/commits?author=ctrlplusb) [📖](https://github.com/ctrlplusb/react-universally/commits?author=ctrlplusb) 💡 👀 [⚠️](https://github.com/ctrlplusb/react-universally/commits?author=ctrlplusb) 🔧 | [
Steven Truesdell](https://steventruesdell.com)
💬 [🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Astrues) [💻](https://github.com/ctrlplusb/react-universally/commits?author=strues) [📖](https://github.com/ctrlplusb/react-universally/commits?author=strues) [⚠️](https://github.com/ctrlplusb/react-universally/commits?author=strues) | [
Thomas Leitgeb](https://twitter.com/_datoml)
[🐛](https://github.com/ctrlplusb/react-universally/issues?q=author%3Adatoml) [💻](https://github.com/ctrlplusb/react-universally/commits?author=datoml) | [
Tyler Nieman](http://tsnieman.net/)
[💻](https://github.com/ctrlplusb/react-universally/commits?author=tsnieman) | 77 | 78 | 79 | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! 80 | -------------------------------------------------------------------------------- /client/components/ReactHotLoader.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | /* eslint-disable import/no-extraneous-dependencies */ 3 | 4 | import React from 'react'; 5 | 6 | // We create this wrapper so that we only import react-hot-loader for a 7 | // development build. Small savings. :) 8 | const ReactHotLoader = process.env.NODE_ENV === 'development' 9 | ? require('react-hot-loader').AppContainer 10 | : ({ children }) => React.Children.only(children); 11 | 12 | export default ReactHotLoader; 13 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import BrowserRouter from 'react-router-dom/BrowserRouter'; 6 | import asyncBootstrapper from 'react-async-bootstrapper'; 7 | import { AsyncComponentProvider } from 'react-async-component'; 8 | 9 | import './polyfills'; 10 | 11 | import ReactHotLoader from './components/ReactHotLoader'; 12 | import DemoApp from '../shared/components/DemoApp'; 13 | 14 | // Get the DOM Element that will host our React application. 15 | const container = document.querySelector('#app'); 16 | 17 | // Does the user's browser support the HTML5 history API? 18 | // If the user's browser doesn't support the HTML5 history API then we 19 | // will force full page refreshes on each page change. 20 | const supportsHistory = 'pushState' in window.history; 21 | 22 | // Get any rehydrateState for the async components. 23 | // eslint-disable-next-line no-underscore-dangle 24 | const asyncComponentsRehydrateState = window.__ASYNC_COMPONENTS_REHYDRATE_STATE__; 25 | 26 | /** 27 | * Renders the given React Application component. 28 | */ 29 | function renderApp(TheApp) { 30 | // Firstly, define our full application component, wrapping the given 31 | // component app with a browser based version of react router. 32 | const app = ( 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ); 41 | 42 | // We use the react-async-component in order to support code splitting of 43 | // our bundle output. It's important to use this helper. 44 | // @see https://github.com/ctrlplusb/react-async-component 45 | asyncBootstrapper(app).then(() => render(app, container)); 46 | } 47 | 48 | // Execute the first render of our app. 49 | renderApp(DemoApp); 50 | 51 | // This registers our service worker for asset caching and offline support. 52 | // Keep this as the last item, just in case the code execution failed (thanks 53 | // to react-boilerplate for that tip.) 54 | require('./registerServiceWorker'); 55 | 56 | // The following is needed so that we can support hot reloading our application. 57 | if (process.env.BUILD_FLAG_IS_DEV === 'true' && module.hot) { 58 | // Accept changes to this file for hot reloading. 59 | module.hot.accept('./index.js'); 60 | // Any changes to our App will cause a hotload re-render. 61 | module.hot.accept('../shared/components/DemoApp', () => { 62 | renderApp(require('../shared/components/DemoApp').default); 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /client/polyfills/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Modernizr from 'modernizr'; 4 | 5 | // This is just an illustrative example. Here you are testing the client's 6 | // support for the "picture" element, and if it isn't supported then you 7 | // load a polyfill. 8 | if (!Modernizr.picture) { 9 | console.log('Client does not support "picture", polyfilling it...'); 10 | // If you want to use the below do a `npm install picturefill -E -S` and then 11 | // uncomment the lines below: 12 | /* 13 | require('picturefill'); 14 | require('picturefill/dist/plugins/mutation/pf.mutation'); 15 | */ 16 | } else { 17 | console.log('Client has support for "picture".'); 18 | } 19 | -------------------------------------------------------------------------------- /client/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We use the offline-plugin to generate a service worker. See the webpack 3 | * config for more details. 4 | * 5 | * We need to ensure that the runtime is installed so that the generated 6 | * service worker is executed. 7 | * 8 | * NOTE: We only enable the service worker for non-development environments. 9 | */ 10 | 11 | import config from '../config'; 12 | 13 | if (process.env.BUILD_FLAG_IS_DEV === 'false') { 14 | // We check the shared config, ensuring that the service worker has been 15 | // enabled. 16 | if (config('serviceWorker.enabled')) { 17 | // eslint-disable-next-line global-require 18 | const OfflinePluginRuntime = require('offline-plugin/runtime'); 19 | 20 | // Install the offline plugin, which instantiates our service worker and app 21 | // cache to support precaching of assets and offline support. 22 | OfflinePluginRuntime.install({ 23 | onUpdating: () => undefined, 24 | // When an update is ready we will tell the new SW to take control immediately. 25 | onUpdateReady: () => OfflinePluginRuntime.applyUpdate(), 26 | // After the new SW update has been applied we will reload the users page 27 | // to ensure they are using the latest assets. 28 | // This only gets run if there were updates available for our cached assets. 29 | onUpdated: () => window.location.reload(), 30 | onUpdateFailed: () => undefined, 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config/components/ClientConfig.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import serialize from 'serialize-javascript'; 4 | import filterWithRules from '../../shared/utils/objects/filterWithRules'; 5 | import values from '../values'; 6 | 7 | // Filter the config down to the properties that are allowed to be included 8 | // in the HTML response. 9 | const clientConfig = filterWithRules( 10 | // These are the rules used to filter the config. 11 | values.clientConfigFilter, 12 | // The config values to filter. 13 | values, 14 | ); 15 | 16 | const serializedClientConfig = serialize(clientConfig); 17 | 18 | /** 19 | * A react component that generates a script tag that binds the allowed 20 | * values to the window so that config values can be read within the 21 | * browser. 22 | * 23 | * They get bound to window.__CLIENT_CONFIG__ 24 | */ 25 | function ClientConfig({ nonce }) { 26 | return ( 27 |