├── .gitignore ├── .travis.yml ├── 01-node-yarn-package-json ├── .gitignore ├── index.js ├── package.json └── yarn.lock ├── 07-socket-io ├── .flowconfig ├── .gitignore ├── .babelrc ├── public │ └── css │ │ └── style.css ├── src │ ├── shared │ │ ├── util.js │ │ ├── component │ │ │ ├── message.jsx │ │ │ ├── button.jsx │ │ │ ├── page │ │ │ │ ├── home.jsx │ │ │ │ ├── not-found.jsx │ │ │ │ ├── hello.jsx │ │ │ │ └── hello-async.jsx │ │ │ └── nav.jsx │ │ ├── routes.test.js │ │ ├── container │ │ │ ├── message.js │ │ │ ├── message-async.js │ │ │ ├── hello-button.js │ │ │ └── hello-async-button.js │ │ ├── routes.js │ │ ├── config.js │ │ ├── reducer │ │ │ ├── hello.js │ │ │ └── hello.test.js │ │ ├── app.jsx │ │ └── action │ │ │ ├── hello.js │ │ │ └── hello.test.js │ ├── server │ │ ├── controller.js │ │ ├── init-store.js │ │ ├── index.js │ │ ├── socket.js │ │ ├── routing.js │ │ └── render-app.jsx │ └── client │ │ ├── socket.js │ │ └── index.jsx ├── .eslintrc.json ├── webpack.config.babel.js └── package.json ├── 03-express-nodemon-pm2 ├── .babelrc ├── .gitignore ├── .flowconfig ├── public │ └── css │ │ └── style.css ├── src │ ├── shared │ │ ├── util.js │ │ └── config.js │ └── server │ │ ├── render-app.js │ │ └── index.js ├── .eslintrc.json └── package.json ├── 02-babel-es6-eslint-flow-jest-husky ├── .gitignore ├── .babelrc ├── .flowconfig ├── src │ ├── dog.test.js │ ├── index.js │ └── dog.js ├── .eslintrc.json └── package.json ├── 04-webpack-react-hmr ├── .flowconfig ├── .gitignore ├── src │ ├── client │ │ ├── app.jsx │ │ └── index.jsx │ ├── shared │ │ ├── util.js │ │ └── config.js │ └── server │ │ ├── render-app.js │ │ └── index.js ├── public │ └── css │ │ └── style.css ├── .babelrc ├── .eslintrc.json ├── webpack.config.babel.js └── package.json ├── 05-redux-immutable-fetch ├── .flowconfig ├── .gitignore ├── public │ └── css │ │ └── style.css ├── src │ ├── shared │ │ ├── util.js │ │ ├── routes.js │ │ ├── routes.test.js │ │ └── config.js │ ├── client │ │ ├── component │ │ │ ├── message.jsx │ │ │ └── button.jsx │ │ ├── container │ │ │ ├── message.js │ │ │ ├── message-async.js │ │ │ ├── hello-button.js │ │ │ └── hello-async-button.js │ │ ├── app.jsx │ │ ├── reducer │ │ │ ├── hello.js │ │ │ └── hello.test.js │ │ ├── action │ │ │ ├── hello.js │ │ │ └── hello.test.js │ │ └── index.jsx │ └── server │ │ ├── render-app.js │ │ └── index.js ├── .babelrc ├── .eslintrc.json ├── webpack.config.babel.js └── package.json ├── 06-react-router-ssr-helmet ├── .flowconfig ├── .gitignore ├── src │ ├── shared │ │ ├── util.js │ │ ├── component │ │ │ ├── message.jsx │ │ │ ├── button.jsx │ │ │ ├── page │ │ │ │ ├── home.jsx │ │ │ │ ├── not-found.jsx │ │ │ │ ├── hello.jsx │ │ │ │ └── hello-async.jsx │ │ │ └── nav.jsx │ │ ├── routes.test.js │ │ ├── container │ │ │ ├── message.js │ │ │ ├── message-async.js │ │ │ ├── hello-button.js │ │ │ └── hello-async-button.js │ │ ├── routes.js │ │ ├── config.js │ │ ├── reducer │ │ │ ├── hello.js │ │ │ └── hello.test.js │ │ ├── app.jsx │ │ └── action │ │ │ ├── hello.js │ │ │ └── hello.test.js │ ├── server │ │ ├── controller.js │ │ ├── index.js │ │ ├── init-store.js │ │ ├── routing.js │ │ └── render-app.jsx │ └── client │ │ └── index.jsx ├── .babelrc ├── public │ └── css │ │ └── style.css ├── .eslintrc.json ├── webpack.config.babel.js └── package.json ├── yarn.lock ├── README.md ├── package.json └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: node 3 | -------------------------------------------------------------------------------- /01-node-yarn-package-json/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /07-socket-io/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | /lib/ 6 | -------------------------------------------------------------------------------- /07-socket-io/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | /lib/ 6 | /dist/ 7 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | /lib/ 6 | /dist/ 7 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | /lib/ 6 | /dist/ 7 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.log 3 | node_modules/ 4 | /coverage/ 5 | /lib/ 6 | /dist/ 7 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/.flowconfig: -------------------------------------------------------------------------------- 1 | [options] 2 | suppress_comment= \\(.\\|\n\\)*\\flow-disable-next-line 3 | -------------------------------------------------------------------------------- /01-node-yarn-package-json/index.js: -------------------------------------------------------------------------------- 1 | const color = require('color') 2 | 3 | const redHexa = color({ r: 255, g: 0, b: 0 }).hex() 4 | 5 | console.log(redHexa) 6 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/client/app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | const App = () =>

Hello React!

6 | 7 | export default App 8 | -------------------------------------------------------------------------------- /07-socket-io/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "flow-react-proptypes" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /07-socket-io/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 960px; 3 | margin: auto; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1 { 8 | color: limegreen; 9 | } 10 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const isProd = process.env.NODE_ENV === 'production' 5 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 960px; 3 | margin: auto; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1 { 8 | color: limegreen; 9 | } 10 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/shared/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const isProd = process.env.NODE_ENV === 'production' 5 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 960px; 3 | margin: auto; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1 { 8 | color: limegreen; 9 | } 10 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/src/shared/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const isProd = process.env.NODE_ENV === 'production' 5 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "flow-react-proptypes" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 960px; 3 | margin: auto; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1 { 8 | color: limegreen; 9 | } 10 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/shared/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const isProd = process.env.NODE_ENV === 'production' 5 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/util.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const isProd = process.env.NODE_ENV === 'production' 5 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "flow-react-proptypes" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "flow", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "flow-react-proptypes" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 960px; 3 | margin: auto; 4 | font-family: sans-serif; 5 | } 6 | 7 | h1 { 8 | color: limegreen; 9 | } 10 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/src/shared/config.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const WEB_PORT = process.env.PORT || 8000 4 | export const STATIC_PATH = '/static' 5 | export const APP_NAME = 'Hello App' 6 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/shared/routes.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const helloEndpointRoute = (num: ?number) => `/ajax/hello/${num || ':num'}` 5 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/src/dog.test.js: -------------------------------------------------------------------------------- 1 | import Dog from './dog' 2 | 3 | test('Dog.bark', () => { 4 | const testDog = new Dog('Test') 5 | expect(testDog.bark()).toBe('Wah wah, I am Test') 6 | }) 7 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /* eslint-disable no-console */ 4 | 5 | import Dog from './dog' 6 | 7 | const toby = new Dog('Toby') 8 | 9 | console.log(toby.bark()) 10 | -------------------------------------------------------------------------------- /01-node-yarn-package-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "node ." 7 | }, 8 | "dependencies": { 9 | "color": "^1.0.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/message.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | message: string, 7 | } 8 | 9 | const Message = ({ message }: Props) => 10 |

{message}

11 | 12 | export default Message 13 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/routes.test.js: -------------------------------------------------------------------------------- 1 | import { helloEndpointRoute } from './routes' 2 | 3 | test('helloEndpointRoute', () => { 4 | expect(helloEndpointRoute()).toBe('/ajax/hello/:num') 5 | expect(helloEndpointRoute(123)).toBe('/ajax/hello/123') 6 | }) 7 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/shared/routes.test.js: -------------------------------------------------------------------------------- 1 | import { helloEndpointRoute } from './routes' 2 | 3 | test('helloEndpointRoute', () => { 4 | expect(helloEndpointRoute()).toBe('/ajax/hello/:num') 5 | expect(helloEndpointRoute(123)).toBe('/ajax/hello/123') 6 | }) 7 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/component/message.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | message: string, 7 | } 8 | 9 | const Message = ({ message }: Props) => 10 |

{message}

11 | 12 | export default Message 13 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/message.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | message: string, 7 | } 8 | 9 | const Message = ({ message }: Props) => 10 |

{message}

11 | 12 | export default Message 13 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/routes.test.js: -------------------------------------------------------------------------------- 1 | import { helloEndpointRoute } from './routes' 2 | 3 | test('helloEndpointRoute', () => { 4 | expect(helloEndpointRoute()).toBe('/ajax/hello/:num') 5 | expect(helloEndpointRoute(123)).toBe('/ajax/hello/123') 6 | }) 7 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/src/dog.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | class Dog { 4 | name: string 5 | 6 | constructor(name: string) { 7 | this.name = name 8 | } 9 | 10 | bark() { 11 | return `Wah wah, I am ${this.name}` 12 | } 13 | } 14 | 15 | export default Dog 16 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/container/message.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import Message from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('message'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(Message) 12 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/container/message.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import Message from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('message'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(Message) 12 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/button.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | label: string, 7 | handleClick: Function, 8 | } 9 | 10 | const Button = ({ label, handleClick }: Props) => 11 | 12 | 13 | export default Button 14 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/container/message.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import Message from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('message'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(Message) 12 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/routes.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const HOME_PAGE_ROUTE = '/' 4 | export const HELLO_PAGE_ROUTE = '/hello' 5 | export const HELLO_ASYNC_PAGE_ROUTE = '/hello-async' 6 | export const NOT_FOUND_DEMO_PAGE_ROUTE = '/404' 7 | 8 | export const helloEndpointRoute = (num: ?number) => `/ajax/hello/${num || ':num'}` 9 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/component/button.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | label: string, 7 | handleClick: Function, 8 | } 9 | 10 | const Button = ({ label, handleClick }: Props) => 11 | 12 | 13 | export default Button 14 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/button.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | 5 | type Props = { 6 | label: string, 7 | handleClick: Function, 8 | } 9 | 10 | const Button = ({ label, handleClick }: Props) => 11 | 12 | 13 | export default Button 14 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/container/message-async.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import MessageAsync from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('messageAsync'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(MessageAsync) 12 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/routes.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const HOME_PAGE_ROUTE = '/' 4 | export const HELLO_PAGE_ROUTE = '/hello' 5 | export const HELLO_ASYNC_PAGE_ROUTE = '/hello-async' 6 | export const NOT_FOUND_DEMO_PAGE_ROUTE = '/404' 7 | 8 | export const helloEndpointRoute = (num: ?number) => `/ajax/hello/${num || ':num'}` 9 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/container/message-async.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import MessageAsync from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('messageAsync'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(MessageAsync) 12 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/container/message-async.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import MessageAsync from '../component/message' 6 | 7 | const mapStateToProps = state => ({ 8 | message: state.hello.get('messageAsync'), 9 | }) 10 | 11 | export default connect(mapStateToProps)(MessageAsync) 12 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/shared/config.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const WEB_PORT = process.env.PORT || 8000 4 | export const WDS_PORT = 7000 5 | export const STATIC_PATH = '/static' 6 | 7 | export const APP_NAME = 'Hello App' 8 | 9 | export const APP_CONTAINER_CLASS = 'js-app' 10 | export const APP_CONTAINER_SELECTOR = `.${APP_CONTAINER_CLASS}` 11 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/shared/config.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const WEB_PORT = process.env.PORT || 8000 4 | export const WDS_PORT = 7000 5 | export const STATIC_PATH = '/static' 6 | 7 | export const APP_NAME = 'Hello App' 8 | 9 | export const APP_CONTAINER_CLASS = 'js-app' 10 | export const APP_CONTAINER_SELECTOR = `.${APP_CONTAINER_CLASS}` 11 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/config.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const WEB_PORT = process.env.PORT || 8000 4 | export const WDS_PORT = 7000 5 | export const STATIC_PATH = '/static' 6 | 7 | export const APP_NAME = 'Hello App' 8 | 9 | export const APP_CONTAINER_CLASS = 'js-app' 10 | export const APP_CONTAINER_SELECTOR = `.${APP_CONTAINER_CLASS}` 11 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "jest": true 12 | }, 13 | "rules": { 14 | "semi": [2, "never"], 15 | "no-unexpected-multiline": 2, 16 | "compat/compat": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "jest": true 12 | }, 13 | "rules": { 14 | "semi": [2, "never"], 15 | "no-unexpected-multiline": 2, 16 | "compat/compat": 2 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "jest": true 13 | }, 14 | "rules": { 15 | "semi": [2, "never"], 16 | "no-unexpected-multiline": 2, 17 | "compat/compat": 2 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/src/server/render-app.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { STATIC_PATH } from '../shared/config' 4 | 5 | const renderApp = (title: string) => 6 | ` 7 | 8 | 9 | ${title} 10 | 11 | 12 | 13 |

${title}

14 | 15 | 16 | ` 17 | 18 | export default renderApp 19 | -------------------------------------------------------------------------------- /07-socket-io/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "jest": true 13 | }, 14 | "settings": { 15 | "polyfills": ["fetch"] 16 | }, 17 | "rules": { 18 | "semi": [2, "never"], 19 | "no-unexpected-multiline": 2, 20 | "compat/compat": 2 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "jest": true 13 | }, 14 | "settings": { 15 | "polyfills": ["fetch"] 16 | }, 17 | "rules": { 18 | "semi": [2, "never"], 19 | "no-unexpected-multiline": 2, 20 | "compat/compat": 2 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype", 8 | "compat" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "jest": true 13 | }, 14 | "settings": { 15 | "polyfills": ["fetch"] 16 | }, 17 | "rules": { 18 | "semi": [2, "never"], 19 | "no-unexpected-multiline": 2, 20 | "compat/compat": 2 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /07-socket-io/src/server/controller.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const homePage = () => null 4 | 5 | export const helloPage = () => ({ 6 | hello: { message: 'Server-side preloaded message' }, 7 | }) 8 | 9 | export const helloAsyncPage = () => ({ 10 | hello: { messageAsync: 'Server-side preloaded message for async page' }, 11 | }) 12 | 13 | export const helloEndpoint = (num: number) => ({ 14 | serverMessage: `Hello from the server! (received ${num})`, 15 | }) 16 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/container/hello-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHello } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHello('Hello!')) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/server/controller.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const homePage = () => null 4 | 5 | export const helloPage = () => ({ 6 | hello: { message: 'Server-side preloaded message' }, 7 | }) 8 | 9 | export const helloAsyncPage = () => ({ 10 | hello: { messageAsync: 'Server-side preloaded message for async page' }, 11 | }) 12 | 13 | export const helloEndpoint = (num: number) => ({ 14 | serverMessage: `Hello from the server! (received ${num})`, 15 | }) 16 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/container/hello-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHello } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHello('Hello!')) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/container/hello-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHello } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHello('Hello!')) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/page/home.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import { APP_NAME } from '../../config' 7 | 8 | const HomePage = () => 9 |
10 | 16 |

{APP_NAME}

17 |
18 | 19 | export default HomePage 20 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/page/not-found.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | const title = 'Page Not Found' 7 | 8 | const NotFoundPage = () => 9 |
10 | 17 |

{title}

18 |
19 | 20 | export default NotFoundPage 21 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/page/home.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import { APP_NAME } from '../../config' 7 | 8 | const HomePage = () => 9 |
10 | 16 |

{APP_NAME}

17 |
18 | 19 | export default HomePage 20 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/container/hello-async-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHelloAsync } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello asynchronously and send 1234', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHelloAsync(1234)) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/page/not-found.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | const title = 'Page Not Found' 7 | 8 | const NotFoundPage = () => 9 |
10 | 17 |

{title}

18 |
19 | 20 | export default NotFoundPage 21 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/container/hello-async-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHelloAsync } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello asynchronously and send 1234', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHelloAsync(1234)) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/container/hello-async-button.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { connect } from 'react-redux' 4 | 5 | import { sayHelloAsync } from '../action/hello' 6 | import Button from '../component/button' 7 | 8 | const mapStateToProps = () => ({ 9 | label: 'Say hello asynchronously and send 1234', 10 | }) 11 | 12 | const mapDispatchToProps = dispatch => ({ 13 | handleClick: () => { dispatch(sayHelloAsync(1234)) }, 14 | }) 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(Button) 17 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import HelloButton from './container/hello-button' 5 | import HelloAsyncButton from './container/hello-async-button' 6 | import Message from './container/message' 7 | import MessageAsync from './container/message-async' 8 | import { APP_NAME } from '../shared/config' 9 | 10 | const App = () => 11 |
12 |

{APP_NAME}

13 | 14 | 15 | 16 | 17 |
18 | 19 | export default App 20 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/config.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export const WEB_PORT = process.env.PORT || 8000 4 | export const WDS_PORT = 7000 5 | export const STATIC_PATH = '/static' 6 | 7 | export const APP_NAME = 'Hello App' 8 | 9 | export const APP_CONTAINER_CLASS = 'js-app' 10 | export const APP_CONTAINER_SELECTOR = `.${APP_CONTAINER_CLASS}` 11 | 12 | export const IO_CONNECT = 'connect' 13 | export const IO_DISCONNECT = 'disconnect' 14 | export const IO_CLIENT_HELLO = 'IO_CLIENT_HELLO' 15 | export const IO_CLIENT_JOIN_ROOM = 'IO_CLIENT_JOIN_ROOM' 16 | export const IO_SERVER_HELLO = 'IO_SERVER_HELLO' 17 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/server/render-app.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { APP_CONTAINER_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 4 | import { isProd } from '../shared/util' 5 | 6 | const renderApp = (title: string) => 7 | ` 8 | 9 | 10 | ${title} 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ` 19 | 20 | export default renderApp 21 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/server/render-app.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { APP_CONTAINER_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 4 | import { isProd } from '../shared/util' 5 | 6 | const renderApp = (title: string) => 7 | ` 8 | 9 | 10 | ${title} 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | ` 19 | 20 | export default renderApp 21 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/page/hello.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import HelloButton from '../../container/hello-button' 7 | import Message from '../../container/message' 8 | 9 | const title = 'Hello Page' 10 | 11 | const HelloPage = () => 12 |
13 | 20 |

{title}

21 | 22 | 23 |
24 | 25 | export default HelloPage 26 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/page/hello.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import HelloButton from '../../container/hello-button' 7 | import Message from '../../container/message' 8 | 9 | const title = 'Hello Page' 10 | 11 | const HelloPage = () => 12 |
13 | 20 |

{title}

21 | 22 | 23 |
24 | 25 | export default HelloPage 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JS-Stack-from-Scratch Walkthrough 2 | 3 | [![Build Status](https://img.shields.io/travis/verekia/js-stack-walkthrough.svg?style=flat-square)](https://travis-ci.org/verekia/js-stack-walkthrough) 4 | 5 | Code for each chapter of the JavaScript-Stack-from-Scratch tutorial. 6 | 7 | The last 2 chapters are in the JS-Stack-Boilerplate repository: 8 | 9 | - [08-bootstrap-jss](https://github.com/verekia/js-stack-boilerplate/tree/master-no-services) 10 | - [09-travis-coveralls-heroku](https://github.com/verekia/js-stack-boilerplate) 11 | 12 | ## Credits 13 | 14 | Created by [@verekia](https://twitter.com/verekia) – [verekia.com](http://verekia.com/). 15 | 16 | License: MIT 17 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/server/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import compression from 'compression' 4 | import express from 'express' 5 | 6 | import routing from './routing' 7 | import { WEB_PORT, STATIC_PATH } from '../shared/config' 8 | import { isProd } from '../shared/util' 9 | 10 | const app = express() 11 | 12 | app.use(compression()) 13 | app.use(STATIC_PATH, express.static('dist')) 14 | app.use(STATIC_PATH, express.static('public')) 15 | 16 | routing(app) 17 | 18 | app.listen(WEB_PORT, () => { 19 | // eslint-disable-next-line no-console 20 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 21 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 22 | }) 23 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/page/hello-async.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import HelloAsyncButton from '../../container/hello-async-button' 7 | import MessageAsync from '../../container/message-async' 8 | 9 | const title = 'Async Hello Page' 10 | 11 | const HelloAsyncPage = () => 12 |
13 | 20 |

{title}

21 | 22 | 23 |
24 | 25 | export default HelloAsyncPage 26 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/src/server/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import compression from 'compression' 4 | import express from 'express' 5 | 6 | import { APP_NAME, STATIC_PATH, WEB_PORT } from '../shared/config' 7 | import { isProd } from '../shared/util' 8 | import renderApp from './render-app' 9 | 10 | const app = express() 11 | 12 | app.use(compression()) 13 | app.use(STATIC_PATH, express.static('dist')) 14 | app.use(STATIC_PATH, express.static('public')) 15 | 16 | app.get('/', (req, res) => { 17 | res.send(renderApp(APP_NAME)) 18 | }) 19 | 20 | app.listen(WEB_PORT, () => { 21 | // eslint-disable-next-line no-console 22 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : '(development)'}.`) 23 | }) 24 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/page/hello-async.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | 6 | import HelloAsyncButton from '../../container/hello-async-button' 7 | import MessageAsync from '../../container/message-async' 8 | 9 | const title = 'Async Hello Page' 10 | 11 | const HelloAsyncPage = () => 12 |
13 | 20 |

{title}

21 | 22 | 23 |
24 | 25 | export default HelloAsyncPage 26 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/server/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import compression from 'compression' 4 | import express from 'express' 5 | 6 | import { APP_NAME, STATIC_PATH, WEB_PORT } from '../shared/config' 7 | import { isProd } from '../shared/util' 8 | import renderApp from './render-app' 9 | 10 | const app = express() 11 | 12 | app.use(compression()) 13 | app.use(STATIC_PATH, express.static('dist')) 14 | app.use(STATIC_PATH, express.static('public')) 15 | 16 | app.get('/', (req, res) => { 17 | res.send(renderApp(APP_NAME)) 18 | }) 19 | 20 | app.listen(WEB_PORT, () => { 21 | // eslint-disable-next-line no-console 22 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 23 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 24 | }) 25 | -------------------------------------------------------------------------------- /07-socket-io/src/server/init-store.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Immutable from 'immutable' 4 | import { createStore, combineReducers, applyMiddleware } from 'redux' 5 | import thunkMiddleware from 'redux-thunk' 6 | 7 | import helloReducer from '../shared/reducer/hello' 8 | 9 | const initStore = (plainPartialState: ?Object) => { 10 | const preloadedState = plainPartialState ? {} : undefined 11 | 12 | if (plainPartialState && plainPartialState.hello) { 13 | // flow-disable-next-line 14 | preloadedState.hello = helloReducer(undefined, {}) 15 | .merge(Immutable.fromJS(plainPartialState.hello)) 16 | } 17 | 18 | return createStore(combineReducers({ hello: helloReducer }), 19 | preloadedState, applyMiddleware(thunkMiddleware)) 20 | } 21 | 22 | export default initStore 23 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/src/client/index.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'babel-polyfill' 4 | 5 | import React from 'react' 6 | import ReactDOM from 'react-dom' 7 | import { AppContainer } from 'react-hot-loader' 8 | 9 | import App from './app' 10 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 11 | 12 | const rootEl = document.querySelector(APP_CONTAINER_SELECTOR) 13 | 14 | const wrapApp = AppComponent => 15 | 16 | 17 | 18 | 19 | ReactDOM.render(wrapApp(App), rootEl) 20 | 21 | if (module.hot) { 22 | // flow-disable-next-line 23 | module.hot.accept('./app', () => { 24 | // eslint-disable-next-line global-require 25 | const NextApp = require('./app').default 26 | ReactDOM.render(wrapApp(NextApp), rootEl) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/server/init-store.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Immutable from 'immutable' 4 | import { createStore, combineReducers, applyMiddleware } from 'redux' 5 | import thunkMiddleware from 'redux-thunk' 6 | 7 | import helloReducer from '../shared/reducer/hello' 8 | 9 | const initStore = (plainPartialState: ?Object) => { 10 | const preloadedState = plainPartialState ? {} : undefined 11 | 12 | if (plainPartialState && plainPartialState.hello) { 13 | // flow-disable-next-line 14 | preloadedState.hello = helloReducer(undefined, {}) 15 | .merge(Immutable.fromJS(plainPartialState.hello)) 16 | } 17 | 18 | return createStore(combineReducers({ hello: helloReducer }), 19 | preloadedState, applyMiddleware(thunkMiddleware)) 20 | } 21 | 22 | export default initStore 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-stack-from-scratch", 3 | "version": "2.4.5", 4 | "repository": "verekia/js-stack-walkthrough", 5 | "author": "Jonathan Verrecchia - @verekia", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "yarn 01 && yarn 02 && yarn 03 && yarn 04 && yarn 05 && yarn 06 && yarn 07", 9 | "01": "cd 01-node-yarn-package-json && yarn && yarn start", 10 | "02": "cd 02-babel-es6-eslint-flow-jest-husky && yarn && yarn test && yarn start", 11 | "03": "cd 03-express-nodemon-pm2 && yarn && yarn test", 12 | "04": "cd 04-webpack-react-hmr && yarn && yarn prepush", 13 | "05": "cd 05-redux-immutable-fetch && yarn && yarn prepush", 14 | "06": "cd 06-react-router-ssr-helmet && yarn && yarn prepush", 15 | "07": "cd 07-socket-io && yarn && yarn prepush" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/component/nav.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import { NavLink } from 'react-router-dom' 5 | import { 6 | HOME_PAGE_ROUTE, 7 | HELLO_PAGE_ROUTE, 8 | HELLO_ASYNC_PAGE_ROUTE, 9 | NOT_FOUND_DEMO_PAGE_ROUTE, 10 | } from '../routes' 11 | 12 | const Nav = () => 13 | 27 | 28 | export default Nav 29 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/component/nav.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import { NavLink } from 'react-router-dom' 5 | import { 6 | HOME_PAGE_ROUTE, 7 | HELLO_PAGE_ROUTE, 8 | HELLO_ASYNC_PAGE_ROUTE, 9 | NOT_FOUND_DEMO_PAGE_ROUTE, 10 | } from '../routes' 11 | 12 | const Nav = () => 13 | 27 | 28 | export default Nav 29 | -------------------------------------------------------------------------------- /07-socket-io/src/server/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import compression from 'compression' 4 | import express from 'express' 5 | import { Server } from 'http' 6 | import socketIO from 'socket.io' 7 | 8 | import routing from './routing' 9 | import { WEB_PORT, STATIC_PATH } from '../shared/config' 10 | import { isProd } from '../shared/util' 11 | import setUpSocket from './socket' 12 | 13 | const app = express() 14 | // flow-disable-next-line 15 | const http = Server(app) 16 | const io = socketIO(http) 17 | setUpSocket(io) 18 | 19 | app.use(compression()) 20 | app.use(STATIC_PATH, express.static('dist')) 21 | app.use(STATIC_PATH, express.static('public')) 22 | 23 | routing(app) 24 | 25 | http.listen(WEB_PORT, () => { 26 | // eslint-disable-next-line no-console 27 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 28 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 29 | }) 30 | -------------------------------------------------------------------------------- /07-socket-io/src/client/socket.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import socketIOClient from 'socket.io-client' 4 | 5 | import { 6 | IO_CONNECT, 7 | IO_DISCONNECT, 8 | IO_CLIENT_HELLO, 9 | IO_CLIENT_JOIN_ROOM, 10 | IO_SERVER_HELLO, 11 | } from '../shared/config' 12 | 13 | const socket = socketIOClient(window.location.host) 14 | 15 | /* eslint-disable no-console */ 16 | // eslint-disable-next-line no-unused-vars 17 | const setUpSocket = (store: Object) => { 18 | socket.on(IO_CONNECT, () => { 19 | console.log('[socket.io] Connected.') 20 | socket.emit(IO_CLIENT_JOIN_ROOM, 'hello-1234') 21 | socket.emit(IO_CLIENT_HELLO, 'Hello!') 22 | }) 23 | 24 | socket.on(IO_SERVER_HELLO, (serverMessage) => { 25 | console.log(`[socket.io] Server: ${serverMessage}`) 26 | }) 27 | 28 | socket.on(IO_DISCONNECT, () => { 29 | console.log('[socket.io] Disconnected.') 30 | }) 31 | } 32 | /* eslint-enable no-console */ 33 | 34 | export default setUpSocket 35 | -------------------------------------------------------------------------------- /02-babel-es6-eslint-flow-jest-husky/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "babel-node src", 8 | "test": "eslint src && flow && jest --coverage", 9 | "precommit": "yarn test", 10 | "prepush": "yarn test" 11 | }, 12 | "dependencies": {}, 13 | "devDependencies": { 14 | "babel-cli": "^6.24.0", 15 | "babel-eslint": "^7.1.1", 16 | "babel-jest": "^19.0.0", 17 | "babel-preset-env": "^1.2.1", 18 | "babel-preset-flow": "^6.23.0", 19 | "eslint": "^3.17.1", 20 | "eslint-config-airbnb": "^14.1.0", 21 | "eslint-plugin-compat": "^1.0.2", 22 | "eslint-plugin-flowtype": "^2.30.3", 23 | "eslint-plugin-import": "^2.2.0", 24 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 25 | "eslint-plugin-react": "^6.9.0", 26 | "flow-bin": "^0.41.0", 27 | "husky": "^0.13.2", 28 | "jest": "^19.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/server/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import compression from 'compression' 4 | import express from 'express' 5 | 6 | import { APP_NAME, STATIC_PATH, WEB_PORT } from '../shared/config' 7 | import { helloEndpointRoute } from '../shared/routes' 8 | import { isProd } from '../shared/util' 9 | import renderApp from './render-app' 10 | 11 | const app = express() 12 | 13 | app.use(compression()) 14 | app.use(STATIC_PATH, express.static('dist')) 15 | app.use(STATIC_PATH, express.static('public')) 16 | 17 | app.get('/', (req, res) => { 18 | res.send(renderApp(APP_NAME)) 19 | }) 20 | 21 | app.get(helloEndpointRoute(), (req, res) => { 22 | res.json({ serverMessage: `Hello from the server! (received ${req.params.num})` }) 23 | }) 24 | 25 | app.listen(WEB_PORT, () => { 26 | // eslint-disable-next-line no-console 27 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 28 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 29 | }) 30 | -------------------------------------------------------------------------------- /07-socket-io/src/server/socket.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | IO_CONNECT, 5 | IO_DISCONNECT, 6 | IO_CLIENT_JOIN_ROOM, 7 | IO_CLIENT_HELLO, 8 | IO_SERVER_HELLO, 9 | } from '../shared/config' 10 | 11 | /* eslint-disable no-console */ 12 | const setUpSocket = (io: Object) => { 13 | io.on(IO_CONNECT, (socket) => { 14 | console.log('[socket.io] A client connected.') 15 | 16 | socket.on(IO_CLIENT_JOIN_ROOM, (room) => { 17 | socket.join(room) 18 | console.log(`[socket.io] A client joined room ${room}.`) 19 | 20 | io.emit(IO_SERVER_HELLO, 'Hello everyone!') 21 | io.to(room).emit(IO_SERVER_HELLO, `Hello clients of room ${room}!`) 22 | socket.emit(IO_SERVER_HELLO, 'Hello you!') 23 | }) 24 | 25 | socket.on(IO_CLIENT_HELLO, (clientMessage) => { 26 | console.log(`[socket.io] Client: ${clientMessage}`) 27 | }) 28 | 29 | socket.on(IO_DISCONNECT, () => { 30 | console.log('[socket.io] A client disconnected.') 31 | }) 32 | }) 33 | } 34 | /* eslint-enable no-console */ 35 | 36 | export default setUpSocket 37 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/reducer/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Immutable from 'immutable' 4 | import type { fromJS as Immut } from 'immutable' 5 | 6 | import { 7 | SAY_HELLO, 8 | SAY_HELLO_ASYNC_REQUEST, 9 | SAY_HELLO_ASYNC_SUCCESS, 10 | SAY_HELLO_ASYNC_FAILURE, 11 | } from '../action/hello' 12 | 13 | const initialState = Immutable.fromJS({ 14 | message: 'Initial reducer message', 15 | messageAsync: 'Initial reducer message for async call', 16 | }) 17 | 18 | const helloReducer = (state: Immut = initialState, action: { type: string, payload: any }) => { 19 | switch (action.type) { 20 | case SAY_HELLO: 21 | return state.set('message', action.payload) 22 | case SAY_HELLO_ASYNC_REQUEST: 23 | return state.set('messageAsync', 'Loading...') 24 | case SAY_HELLO_ASYNC_SUCCESS: 25 | return state.set('messageAsync', action.payload) 26 | case SAY_HELLO_ASYNC_FAILURE: 27 | return state.set('messageAsync', 'No message received, please check your connection') 28 | default: 29 | return state 30 | } 31 | } 32 | 33 | export default helloReducer 34 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | import { Switch } from 'react-router' 6 | import { Route } from 'react-router-dom' 7 | import { APP_NAME } from './config' 8 | import Nav from './component/nav' 9 | import HomePage from './component/page/home' 10 | import HelloPage from './component/page/hello' 11 | import HelloAsyncPage from './component/page/hello-async' 12 | import NotFoundPage from './component/page/not-found' 13 | import { 14 | HOME_PAGE_ROUTE, 15 | HELLO_PAGE_ROUTE, 16 | HELLO_ASYNC_PAGE_ROUTE, 17 | } from './routes' 18 | 19 | const App = () => 20 |
21 | 22 |
30 | 31 | export default App 32 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/reducer/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Immutable from 'immutable' 4 | import type { fromJS as Immut } from 'immutable' 5 | 6 | import { 7 | SAY_HELLO, 8 | SAY_HELLO_ASYNC_REQUEST, 9 | SAY_HELLO_ASYNC_SUCCESS, 10 | SAY_HELLO_ASYNC_FAILURE, 11 | } from '../action/hello' 12 | 13 | const initialState = Immutable.fromJS({ 14 | message: 'Initial reducer message', 15 | messageAsync: 'Initial reducer message for async call', 16 | }) 17 | 18 | const helloReducer = (state: Immut = initialState, action: { type: string, payload: any }) => { 19 | switch (action.type) { 20 | case SAY_HELLO: 21 | return state.set('message', action.payload) 22 | case SAY_HELLO_ASYNC_REQUEST: 23 | return state.set('messageAsync', 'Loading...') 24 | case SAY_HELLO_ASYNC_SUCCESS: 25 | return state.set('messageAsync', action.payload) 26 | case SAY_HELLO_ASYNC_FAILURE: 27 | return state.set('messageAsync', 'No message received, please check your connection') 28 | default: 29 | return state 30 | } 31 | } 32 | 33 | export default helloReducer 34 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/reducer/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import Immutable from 'immutable' 4 | import type { fromJS as Immut } from 'immutable' 5 | 6 | import { 7 | SAY_HELLO, 8 | SAY_HELLO_ASYNC_REQUEST, 9 | SAY_HELLO_ASYNC_SUCCESS, 10 | SAY_HELLO_ASYNC_FAILURE, 11 | } from '../action/hello' 12 | 13 | const initialState = Immutable.fromJS({ 14 | message: 'Initial reducer message', 15 | messageAsync: 'Initial reducer message for async call', 16 | }) 17 | 18 | const helloReducer = (state: Immut = initialState, action: { type: string, payload: any }) => { 19 | switch (action.type) { 20 | case SAY_HELLO: 21 | return state.set('message', action.payload) 22 | case SAY_HELLO_ASYNC_REQUEST: 23 | return state.set('messageAsync', 'Loading...') 24 | case SAY_HELLO_ASYNC_SUCCESS: 25 | return state.set('messageAsync', action.payload) 26 | case SAY_HELLO_ASYNC_FAILURE: 27 | return state.set('messageAsync', 'No message received, please check your connection') 28 | default: 29 | return state 30 | } 31 | } 32 | 33 | export default helloReducer 34 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import Helmet from 'react-helmet' 5 | import { Switch } from 'react-router' 6 | import { Route } from 'react-router-dom' 7 | import { APP_NAME } from './config' 8 | import Nav from './component/nav' 9 | import HomePage from './component/page/home' 10 | import HelloPage from './component/page/hello' 11 | import HelloAsyncPage from './component/page/hello-async' 12 | import NotFoundPage from './component/page/not-found' 13 | import { 14 | HOME_PAGE_ROUTE, 15 | HELLO_PAGE_ROUTE, 16 | HELLO_ASYNC_PAGE_ROUTE, 17 | } from './routes' 18 | 19 | const App = () => 20 |
21 | 22 |
30 | 31 | export default App 32 | -------------------------------------------------------------------------------- /07-socket-io/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import path from 'path' 4 | import webpack from 'webpack' 5 | 6 | import { WDS_PORT } from './src/shared/config' 7 | import { isProd } from './src/shared/util' 8 | 9 | export default { 10 | entry: [ 11 | 'react-hot-loader/patch', 12 | './src/client', 13 | ], 14 | output: { 15 | filename: 'js/bundle.js', 16 | path: path.resolve(__dirname, 'dist'), 17 | publicPath: isProd ? '/static/' : `http://localhost:${WDS_PORT}/dist/`, 18 | }, 19 | module: { 20 | rules: [ 21 | { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ }, 22 | ], 23 | }, 24 | devtool: isProd ? false : 'source-map', 25 | resolve: { 26 | extensions: ['.js', '.jsx'], 27 | }, 28 | devServer: { 29 | port: WDS_PORT, 30 | hot: true, 31 | headers: { 32 | 'Access-Control-Allow-Origin': '*', 33 | }, 34 | }, 35 | plugins: [ 36 | new webpack.optimize.OccurrenceOrderPlugin(), 37 | new webpack.HotModuleReplacementPlugin(), 38 | new webpack.NamedModulesPlugin(), 39 | new webpack.NoEmitOnErrorsPlugin(), 40 | ], 41 | } 42 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import path from 'path' 4 | import webpack from 'webpack' 5 | 6 | import { WDS_PORT } from './src/shared/config' 7 | import { isProd } from './src/shared/util' 8 | 9 | export default { 10 | entry: [ 11 | 'react-hot-loader/patch', 12 | './src/client', 13 | ], 14 | output: { 15 | filename: 'js/bundle.js', 16 | path: path.resolve(__dirname, 'dist'), 17 | publicPath: isProd ? '/static/' : `http://localhost:${WDS_PORT}/dist/`, 18 | }, 19 | module: { 20 | rules: [ 21 | { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ }, 22 | ], 23 | }, 24 | devtool: isProd ? false : 'source-map', 25 | resolve: { 26 | extensions: ['.js', '.jsx'], 27 | }, 28 | devServer: { 29 | port: WDS_PORT, 30 | hot: true, 31 | headers: { 32 | 'Access-Control-Allow-Origin': '*', 33 | }, 34 | }, 35 | plugins: [ 36 | new webpack.optimize.OccurrenceOrderPlugin(), 37 | new webpack.HotModuleReplacementPlugin(), 38 | new webpack.NamedModulesPlugin(), 39 | new webpack.NoEmitOnErrorsPlugin(), 40 | ], 41 | } 42 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import path from 'path' 4 | import webpack from 'webpack' 5 | 6 | import { WDS_PORT } from './src/shared/config' 7 | import { isProd } from './src/shared/util' 8 | 9 | export default { 10 | entry: [ 11 | 'react-hot-loader/patch', 12 | './src/client', 13 | ], 14 | output: { 15 | filename: 'js/bundle.js', 16 | path: path.resolve(__dirname, 'dist'), 17 | publicPath: isProd ? '/static/' : `http://localhost:${WDS_PORT}/dist/`, 18 | }, 19 | module: { 20 | rules: [ 21 | { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ }, 22 | ], 23 | }, 24 | devtool: isProd ? false : 'source-map', 25 | resolve: { 26 | extensions: ['.js', '.jsx'], 27 | }, 28 | devServer: { 29 | port: WDS_PORT, 30 | hot: true, 31 | headers: { 32 | 'Access-Control-Allow-Origin': '*', 33 | }, 34 | }, 35 | plugins: [ 36 | new webpack.optimize.OccurrenceOrderPlugin(), 37 | new webpack.HotModuleReplacementPlugin(), 38 | new webpack.NamedModulesPlugin(), 39 | new webpack.NoEmitOnErrorsPlugin(), 40 | ], 41 | } 42 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import path from 'path' 4 | import webpack from 'webpack' 5 | 6 | import { WDS_PORT } from './src/shared/config' 7 | import { isProd } from './src/shared/util' 8 | 9 | export default { 10 | entry: [ 11 | 'react-hot-loader/patch', 12 | './src/client', 13 | ], 14 | output: { 15 | filename: 'js/bundle.js', 16 | path: path.resolve(__dirname, 'dist'), 17 | publicPath: isProd ? '/static/' : `http://localhost:${WDS_PORT}/dist/`, 18 | }, 19 | module: { 20 | rules: [ 21 | { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ }, 22 | ], 23 | }, 24 | devtool: isProd ? false : 'source-map', 25 | resolve: { 26 | extensions: ['.js', '.jsx'], 27 | }, 28 | devServer: { 29 | port: WDS_PORT, 30 | hot: true, 31 | headers: { 32 | 'Access-Control-Allow-Origin': '*', 33 | }, 34 | }, 35 | plugins: [ 36 | new webpack.optimize.OccurrenceOrderPlugin(), 37 | new webpack.HotModuleReplacementPlugin(), 38 | new webpack.NamedModulesPlugin(), 39 | new webpack.NoEmitOnErrorsPlugin(), 40 | ], 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jonathan Verrecchia 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 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/action/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'isomorphic-fetch' 4 | 5 | import { createAction } from 'redux-actions' 6 | import { helloEndpointRoute } from '../../shared/routes' 7 | 8 | export const SAY_HELLO = 'SAY_HELLO' 9 | export const SAY_HELLO_ASYNC_REQUEST = 'SAY_HELLO_ASYNC_REQUEST' 10 | export const SAY_HELLO_ASYNC_SUCCESS = 'SAY_HELLO_ASYNC_SUCCESS' 11 | export const SAY_HELLO_ASYNC_FAILURE = 'SAY_HELLO_ASYNC_FAILURE' 12 | 13 | export const sayHello = createAction(SAY_HELLO) 14 | export const sayHelloAsyncRequest = createAction(SAY_HELLO_ASYNC_REQUEST) 15 | export const sayHelloAsyncSuccess = createAction(SAY_HELLO_ASYNC_SUCCESS) 16 | export const sayHelloAsyncFailure = createAction(SAY_HELLO_ASYNC_FAILURE) 17 | 18 | export const sayHelloAsync = (num: number) => (dispatch: Function) => { 19 | dispatch(sayHelloAsyncRequest()) 20 | return fetch(helloEndpointRoute(num), { method: 'GET' }) 21 | .then((res) => { 22 | if (!res.ok) throw Error(res.statusText) 23 | return res.json() 24 | }) 25 | .then((data) => { 26 | if (!data.serverMessage) throw Error('No message received') 27 | dispatch(sayHelloAsyncSuccess(data.serverMessage)) 28 | }) 29 | .catch(() => { 30 | dispatch(sayHelloAsyncFailure()) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/action/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'isomorphic-fetch' 4 | 5 | import { createAction } from 'redux-actions' 6 | import { helloEndpointRoute } from '../../shared/routes' 7 | 8 | export const SAY_HELLO = 'SAY_HELLO' 9 | export const SAY_HELLO_ASYNC_REQUEST = 'SAY_HELLO_ASYNC_REQUEST' 10 | export const SAY_HELLO_ASYNC_SUCCESS = 'SAY_HELLO_ASYNC_SUCCESS' 11 | export const SAY_HELLO_ASYNC_FAILURE = 'SAY_HELLO_ASYNC_FAILURE' 12 | 13 | export const sayHello = createAction(SAY_HELLO) 14 | export const sayHelloAsyncRequest = createAction(SAY_HELLO_ASYNC_REQUEST) 15 | export const sayHelloAsyncSuccess = createAction(SAY_HELLO_ASYNC_SUCCESS) 16 | export const sayHelloAsyncFailure = createAction(SAY_HELLO_ASYNC_FAILURE) 17 | 18 | export const sayHelloAsync = (num: number) => (dispatch: Function) => { 19 | dispatch(sayHelloAsyncRequest()) 20 | return fetch(helloEndpointRoute(num), { method: 'GET' }) 21 | .then((res) => { 22 | if (!res.ok) throw Error(res.statusText) 23 | return res.json() 24 | }) 25 | .then((data) => { 26 | if (!data.serverMessage) throw Error('No message received') 27 | dispatch(sayHelloAsyncSuccess(data.serverMessage)) 28 | }) 29 | .catch(() => { 30 | dispatch(sayHelloAsyncFailure()) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/action/hello.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'isomorphic-fetch' 4 | 5 | import { createAction } from 'redux-actions' 6 | import { helloEndpointRoute } from '../../shared/routes' 7 | 8 | export const SAY_HELLO = 'SAY_HELLO' 9 | export const SAY_HELLO_ASYNC_REQUEST = 'SAY_HELLO_ASYNC_REQUEST' 10 | export const SAY_HELLO_ASYNC_SUCCESS = 'SAY_HELLO_ASYNC_SUCCESS' 11 | export const SAY_HELLO_ASYNC_FAILURE = 'SAY_HELLO_ASYNC_FAILURE' 12 | 13 | export const sayHello = createAction(SAY_HELLO) 14 | export const sayHelloAsyncRequest = createAction(SAY_HELLO_ASYNC_REQUEST) 15 | export const sayHelloAsyncSuccess = createAction(SAY_HELLO_ASYNC_SUCCESS) 16 | export const sayHelloAsyncFailure = createAction(SAY_HELLO_ASYNC_FAILURE) 17 | 18 | export const sayHelloAsync = (num: number) => (dispatch: Function) => { 19 | dispatch(sayHelloAsyncRequest()) 20 | return fetch(helloEndpointRoute(num), { method: 'GET' }) 21 | .then((res) => { 22 | if (!res.ok) throw Error(res.statusText) 23 | return res.json() 24 | }) 25 | .then((data) => { 26 | if (!data.serverMessage) throw Error('No message received') 27 | dispatch(sayHelloAsyncSuccess(data.serverMessage)) 28 | }) 29 | .catch(() => { 30 | dispatch(sayHelloAsyncFailure()) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /07-socket-io/src/server/routing.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | homePage, 5 | helloPage, 6 | helloAsyncPage, 7 | helloEndpoint, 8 | } from './controller' 9 | 10 | import { 11 | HOME_PAGE_ROUTE, 12 | HELLO_PAGE_ROUTE, 13 | HELLO_ASYNC_PAGE_ROUTE, 14 | helloEndpointRoute, 15 | } from '../shared/routes' 16 | 17 | import renderApp from './render-app' 18 | 19 | export default (app: Object) => { 20 | app.get(HOME_PAGE_ROUTE, (req, res) => { 21 | res.send(renderApp(req.url, homePage())) 22 | }) 23 | 24 | app.get(HELLO_PAGE_ROUTE, (req, res) => { 25 | res.send(renderApp(req.url, helloPage())) 26 | }) 27 | 28 | app.get(HELLO_ASYNC_PAGE_ROUTE, (req, res) => { 29 | res.send(renderApp(req.url, helloAsyncPage())) 30 | }) 31 | 32 | app.get(helloEndpointRoute(), (req, res) => { 33 | res.json(helloEndpoint(req.params.num)) 34 | }) 35 | 36 | app.get('/500', () => { 37 | throw Error('Fake Internal Server Error') 38 | }) 39 | 40 | app.get('*', (req, res) => { 41 | res.status(404).send(renderApp(req.url)) 42 | }) 43 | 44 | // eslint-disable-next-line no-unused-vars 45 | app.use((err, req, res, next) => { 46 | // eslint-disable-next-line no-console 47 | console.error(err.stack) 48 | res.status(500).send('Something went wrong!') 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/server/routing.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { 4 | homePage, 5 | helloPage, 6 | helloAsyncPage, 7 | helloEndpoint, 8 | } from './controller' 9 | 10 | import { 11 | HOME_PAGE_ROUTE, 12 | HELLO_PAGE_ROUTE, 13 | HELLO_ASYNC_PAGE_ROUTE, 14 | helloEndpointRoute, 15 | } from '../shared/routes' 16 | 17 | import renderApp from './render-app' 18 | 19 | export default (app: Object) => { 20 | app.get(HOME_PAGE_ROUTE, (req, res) => { 21 | res.send(renderApp(req.url, homePage())) 22 | }) 23 | 24 | app.get(HELLO_PAGE_ROUTE, (req, res) => { 25 | res.send(renderApp(req.url, helloPage())) 26 | }) 27 | 28 | app.get(HELLO_ASYNC_PAGE_ROUTE, (req, res) => { 29 | res.send(renderApp(req.url, helloAsyncPage())) 30 | }) 31 | 32 | app.get(helloEndpointRoute(), (req, res) => { 33 | res.json(helloEndpoint(req.params.num)) 34 | }) 35 | 36 | app.get('/500', () => { 37 | throw Error('Fake Internal Server Error') 38 | }) 39 | 40 | app.get('*', (req, res) => { 41 | res.status(404).send(renderApp(req.url)) 42 | }) 43 | 44 | // eslint-disable-next-line no-unused-vars 45 | app.use((err, req, res, next) => { 46 | // eslint-disable-next-line no-console 47 | console.error(err.stack) 48 | res.status(500).send('Something went wrong!') 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/reducer/hello.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | sayHello, 3 | sayHelloAsyncRequest, 4 | sayHelloAsyncSuccess, 5 | sayHelloAsyncFailure, 6 | } from '../action/hello' 7 | 8 | import helloReducer from './hello' 9 | 10 | let helloState 11 | 12 | beforeEach(() => { 13 | helloState = helloReducer(undefined, {}) 14 | }) 15 | 16 | test('handle default', () => { 17 | expect(helloState.get('message')).toBe('Initial reducer message') 18 | expect(helloState.get('messageAsync')).toBe('Initial reducer message for async call') 19 | }) 20 | 21 | test('handle SAY_HELLO', () => { 22 | helloState = helloReducer(helloState, sayHello('Test')) 23 | expect(helloState.get('message')).toBe('Test') 24 | }) 25 | 26 | test('handle SAY_HELLO_ASYNC_REQUEST', () => { 27 | helloState = helloReducer(helloState, sayHelloAsyncRequest()) 28 | expect(helloState.get('messageAsync')).toBe('Loading...') 29 | }) 30 | 31 | test('handle SAY_HELLO_ASYNC_SUCCESS', () => { 32 | helloState = helloReducer(helloState, sayHelloAsyncSuccess('Test async')) 33 | expect(helloState.get('messageAsync')).toBe('Test async') 34 | }) 35 | 36 | test('handle SAY_HELLO_ASYNC_FAILURE', () => { 37 | helloState = helloReducer(helloState, sayHelloAsyncFailure()) 38 | expect(helloState.get('messageAsync')).toBe('No message received, please check your connection') 39 | }) 40 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/reducer/hello.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | sayHello, 3 | sayHelloAsyncRequest, 4 | sayHelloAsyncSuccess, 5 | sayHelloAsyncFailure, 6 | } from '../action/hello' 7 | 8 | import helloReducer from './hello' 9 | 10 | let helloState 11 | 12 | beforeEach(() => { 13 | helloState = helloReducer(undefined, {}) 14 | }) 15 | 16 | test('handle default', () => { 17 | expect(helloState.get('message')).toBe('Initial reducer message') 18 | expect(helloState.get('messageAsync')).toBe('Initial reducer message for async call') 19 | }) 20 | 21 | test('handle SAY_HELLO', () => { 22 | helloState = helloReducer(helloState, sayHello('Test')) 23 | expect(helloState.get('message')).toBe('Test') 24 | }) 25 | 26 | test('handle SAY_HELLO_ASYNC_REQUEST', () => { 27 | helloState = helloReducer(helloState, sayHelloAsyncRequest()) 28 | expect(helloState.get('messageAsync')).toBe('Loading...') 29 | }) 30 | 31 | test('handle SAY_HELLO_ASYNC_SUCCESS', () => { 32 | helloState = helloReducer(helloState, sayHelloAsyncSuccess('Test async')) 33 | expect(helloState.get('messageAsync')).toBe('Test async') 34 | }) 35 | 36 | test('handle SAY_HELLO_ASYNC_FAILURE', () => { 37 | helloState = helloReducer(helloState, sayHelloAsyncFailure()) 38 | expect(helloState.get('messageAsync')).toBe('No message received, please check your connection') 39 | }) 40 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/reducer/hello.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | sayHello, 3 | sayHelloAsyncRequest, 4 | sayHelloAsyncSuccess, 5 | sayHelloAsyncFailure, 6 | } from '../action/hello' 7 | 8 | import helloReducer from './hello' 9 | 10 | let helloState 11 | 12 | beforeEach(() => { 13 | helloState = helloReducer(undefined, {}) 14 | }) 15 | 16 | test('handle default', () => { 17 | expect(helloState.get('message')).toBe('Initial reducer message') 18 | expect(helloState.get('messageAsync')).toBe('Initial reducer message for async call') 19 | }) 20 | 21 | test('handle SAY_HELLO', () => { 22 | helloState = helloReducer(helloState, sayHello('Test')) 23 | expect(helloState.get('message')).toBe('Test') 24 | }) 25 | 26 | test('handle SAY_HELLO_ASYNC_REQUEST', () => { 27 | helloState = helloReducer(helloState, sayHelloAsyncRequest()) 28 | expect(helloState.get('messageAsync')).toBe('Loading...') 29 | }) 30 | 31 | test('handle SAY_HELLO_ASYNC_SUCCESS', () => { 32 | helloState = helloReducer(helloState, sayHelloAsyncSuccess('Test async')) 33 | expect(helloState.get('messageAsync')).toBe('Test async') 34 | }) 35 | 36 | test('handle SAY_HELLO_ASYNC_FAILURE', () => { 37 | helloState = helloReducer(helloState, sayHelloAsyncFailure()) 38 | expect(helloState.get('messageAsync')).toBe('No message received, please check your connection') 39 | }) 40 | -------------------------------------------------------------------------------- /01-node-yarn-package-json/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | color-convert@^1.8.2: 6 | version "1.9.0" 7 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" 8 | dependencies: 9 | color-name "^1.1.1" 10 | 11 | color-name@^1.0.0, color-name@^1.1.1: 12 | version "1.1.1" 13 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 14 | 15 | color-string@^1.4.0: 16 | version "1.5.2" 17 | resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.2.tgz#26e45814bc3c9a7cbd6751648a41434514a773a9" 18 | dependencies: 19 | color-name "^1.0.0" 20 | simple-swizzle "^0.2.2" 21 | 22 | color@^1.0.3: 23 | version "1.0.3" 24 | resolved "https://registry.yarnpkg.com/color/-/color-1.0.3.tgz#e48e832d85f14ef694fb468811c2d5cfe729b55d" 25 | dependencies: 26 | color-convert "^1.8.2" 27 | color-string "^1.4.0" 28 | 29 | is-arrayish@^0.3.1: 30 | version "0.3.1" 31 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.1.tgz#c2dfc386abaa0c3e33c48db3fe87059e69065efd" 32 | 33 | simple-swizzle@^0.2.2: 34 | version "0.2.2" 35 | resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" 36 | dependencies: 37 | is-arrayish "^0.3.1" 38 | -------------------------------------------------------------------------------- /03-express-nodemon-pm2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "yarn dev:start", 8 | "dev:start": "nodemon --ignore lib --exec babel-node src/server", 9 | "prod:build": "rimraf lib && babel src -d lib --ignore .test.js", 10 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 11 | "prod:stop": "pm2 delete server", 12 | "test": "eslint src && flow && jest --coverage", 13 | "precommit": "yarn test", 14 | "prepush": "yarn test && yarn prod:build" 15 | }, 16 | "dependencies": { 17 | "compression": "^1.6.2", 18 | "express": "^4.15.2" 19 | }, 20 | "devDependencies": { 21 | "babel-cli": "^6.24.0", 22 | "babel-eslint": "^7.1.1", 23 | "babel-jest": "^19.0.0", 24 | "babel-preset-env": "^1.2.1", 25 | "babel-preset-flow": "^6.23.0", 26 | "cross-env": "^3.2.3", 27 | "eslint": "^3.17.1", 28 | "eslint-config-airbnb": "^14.1.0", 29 | "eslint-plugin-compat": "^1.0.2", 30 | "eslint-plugin-flowtype": "^2.30.3", 31 | "eslint-plugin-import": "^2.2.0", 32 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 33 | "eslint-plugin-react": "^6.9.0", 34 | "flow-bin": "^0.41.0", 35 | "husky": "^0.13.2", 36 | "jest": "^19.0.2", 37 | "nodemon": "^1.11.0", 38 | "pm2": "^2.4.2", 39 | "rimraf": "^2.6.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/index.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'babel-polyfill' 4 | 5 | import React from 'react' 6 | import ReactDOM from 'react-dom' 7 | import { AppContainer } from 'react-hot-loader' 8 | import { Provider } from 'react-redux' 9 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux' 10 | import thunkMiddleware from 'redux-thunk' 11 | 12 | import App from './app' 13 | import helloReducer from './reducer/hello' 14 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 15 | import { isProd } from '../shared/util' 16 | 17 | // eslint-disable-next-line no-underscore-dangle 18 | const composeEnhancers = (isProd ? null : window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose 19 | 20 | const store = createStore(combineReducers({ hello: helloReducer }), 21 | composeEnhancers(applyMiddleware(thunkMiddleware))) 22 | 23 | const rootEl = document.querySelector(APP_CONTAINER_SELECTOR) 24 | 25 | const wrapApp = (AppComponent, reduxStore) => 26 | 27 | 28 | 29 | 30 | 31 | 32 | ReactDOM.render(wrapApp(App, store), rootEl) 33 | 34 | if (module.hot) { 35 | // flow-disable-next-line 36 | module.hot.accept('./app', () => { 37 | // eslint-disable-next-line global-require 38 | const NextApp = require('./app').default 39 | ReactDOM.render(wrapApp(NextApp, store), rootEl) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /07-socket-io/src/server/render-app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import ReactDOMServer from 'react-dom/server' 5 | import Helmet from 'react-helmet' 6 | import { Provider } from 'react-redux' 7 | import { StaticRouter } from 'react-router' 8 | 9 | import initStore from './init-store' 10 | import App from './../shared/app' 11 | import { APP_CONTAINER_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 12 | import { isProd } from '../shared/util' 13 | 14 | const renderApp = (location: string, plainPartialState: ?Object, routerContext: ?Object = {}) => { 15 | const store = initStore(plainPartialState) 16 | const appHtml = ReactDOMServer.renderToString( 17 | 18 | 19 | 20 | 21 | ) 22 | const head = Helmet.rewind() 23 | 24 | return ( 25 | ` 26 | 27 | 28 | ${head.title} 29 | ${head.meta} 30 | 31 | 32 | 33 |
${appHtml}
34 | 37 | 38 | 39 | ` 40 | ) 41 | } 42 | 43 | export default renderApp 44 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/server/render-app.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react' 4 | import ReactDOMServer from 'react-dom/server' 5 | import Helmet from 'react-helmet' 6 | import { Provider } from 'react-redux' 7 | import { StaticRouter } from 'react-router' 8 | 9 | import initStore from './init-store' 10 | import App from './../shared/app' 11 | import { APP_CONTAINER_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 12 | import { isProd } from '../shared/util' 13 | 14 | const renderApp = (location: string, plainPartialState: ?Object, routerContext: ?Object = {}) => { 15 | const store = initStore(plainPartialState) 16 | const appHtml = ReactDOMServer.renderToString( 17 | 18 | 19 | 20 | 21 | ) 22 | const head = Helmet.rewind() 23 | 24 | return ( 25 | ` 26 | 27 | 28 | ${head.title} 29 | ${head.meta} 30 | 31 | 32 | 33 |
${appHtml}
34 | 37 | 38 | 39 | ` 40 | ) 41 | } 42 | 43 | export default renderApp 44 | -------------------------------------------------------------------------------- /07-socket-io/src/shared/action/hello.test.js: -------------------------------------------------------------------------------- 1 | import fetchMock from 'fetch-mock' 2 | import configureMockStore from 'redux-mock-store' 3 | import thunkMiddleware from 'redux-thunk' 4 | 5 | import { 6 | sayHelloAsync, 7 | sayHelloAsyncRequest, 8 | sayHelloAsyncSuccess, 9 | sayHelloAsyncFailure, 10 | } from './hello' 11 | 12 | import { helloEndpointRoute } from '../../shared/routes' 13 | 14 | const mockStore = configureMockStore([thunkMiddleware]) 15 | 16 | afterEach(() => { 17 | fetchMock.restore() 18 | }) 19 | 20 | test('sayHelloAsync success', () => { 21 | fetchMock.get(helloEndpointRoute(666), { serverMessage: 'Async hello success' }) 22 | const store = mockStore() 23 | return store.dispatch(sayHelloAsync(666)) 24 | .then(() => { 25 | expect(store.getActions()).toEqual([ 26 | sayHelloAsyncRequest(), 27 | sayHelloAsyncSuccess('Async hello success'), 28 | ]) 29 | }) 30 | }) 31 | 32 | test('sayHelloAsync 404', () => { 33 | fetchMock.get(helloEndpointRoute(666), 404) 34 | const store = mockStore() 35 | return store.dispatch(sayHelloAsync(666)) 36 | .then(() => { 37 | expect(store.getActions()).toEqual([ 38 | sayHelloAsyncRequest(), 39 | sayHelloAsyncFailure(), 40 | ]) 41 | }) 42 | }) 43 | 44 | test('sayHelloAsync data error', () => { 45 | fetchMock.get(helloEndpointRoute(666), {}) 46 | const store = mockStore() 47 | return store.dispatch(sayHelloAsync(666)) 48 | .then(() => { 49 | expect(store.getActions()).toEqual([ 50 | sayHelloAsyncRequest(), 51 | sayHelloAsyncFailure(), 52 | ]) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/src/client/action/hello.test.js: -------------------------------------------------------------------------------- 1 | import fetchMock from 'fetch-mock' 2 | import configureMockStore from 'redux-mock-store' 3 | import thunkMiddleware from 'redux-thunk' 4 | 5 | import { 6 | sayHelloAsync, 7 | sayHelloAsyncRequest, 8 | sayHelloAsyncSuccess, 9 | sayHelloAsyncFailure, 10 | } from './hello' 11 | 12 | import { helloEndpointRoute } from '../../shared/routes' 13 | 14 | const mockStore = configureMockStore([thunkMiddleware]) 15 | 16 | afterEach(() => { 17 | fetchMock.restore() 18 | }) 19 | 20 | test('sayHelloAsync success', () => { 21 | fetchMock.get(helloEndpointRoute(666), { serverMessage: 'Async hello success' }) 22 | const store = mockStore() 23 | return store.dispatch(sayHelloAsync(666)) 24 | .then(() => { 25 | expect(store.getActions()).toEqual([ 26 | sayHelloAsyncRequest(), 27 | sayHelloAsyncSuccess('Async hello success'), 28 | ]) 29 | }) 30 | }) 31 | 32 | test('sayHelloAsync 404', () => { 33 | fetchMock.get(helloEndpointRoute(666), 404) 34 | const store = mockStore() 35 | return store.dispatch(sayHelloAsync(666)) 36 | .then(() => { 37 | expect(store.getActions()).toEqual([ 38 | sayHelloAsyncRequest(), 39 | sayHelloAsyncFailure(), 40 | ]) 41 | }) 42 | }) 43 | 44 | test('sayHelloAsync data error', () => { 45 | fetchMock.get(helloEndpointRoute(666), {}) 46 | const store = mockStore() 47 | return store.dispatch(sayHelloAsync(666)) 48 | .then(() => { 49 | expect(store.getActions()).toEqual([ 50 | sayHelloAsyncRequest(), 51 | sayHelloAsyncFailure(), 52 | ]) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/shared/action/hello.test.js: -------------------------------------------------------------------------------- 1 | import fetchMock from 'fetch-mock' 2 | import configureMockStore from 'redux-mock-store' 3 | import thunkMiddleware from 'redux-thunk' 4 | 5 | import { 6 | sayHelloAsync, 7 | sayHelloAsyncRequest, 8 | sayHelloAsyncSuccess, 9 | sayHelloAsyncFailure, 10 | } from './hello' 11 | 12 | import { helloEndpointRoute } from '../../shared/routes' 13 | 14 | const mockStore = configureMockStore([thunkMiddleware]) 15 | 16 | afterEach(() => { 17 | fetchMock.restore() 18 | }) 19 | 20 | test('sayHelloAsync success', () => { 21 | fetchMock.get(helloEndpointRoute(666), { serverMessage: 'Async hello success' }) 22 | const store = mockStore() 23 | return store.dispatch(sayHelloAsync(666)) 24 | .then(() => { 25 | expect(store.getActions()).toEqual([ 26 | sayHelloAsyncRequest(), 27 | sayHelloAsyncSuccess('Async hello success'), 28 | ]) 29 | }) 30 | }) 31 | 32 | test('sayHelloAsync 404', () => { 33 | fetchMock.get(helloEndpointRoute(666), 404) 34 | const store = mockStore() 35 | return store.dispatch(sayHelloAsync(666)) 36 | .then(() => { 37 | expect(store.getActions()).toEqual([ 38 | sayHelloAsyncRequest(), 39 | sayHelloAsyncFailure(), 40 | ]) 41 | }) 42 | }) 43 | 44 | test('sayHelloAsync data error', () => { 45 | fetchMock.get(helloEndpointRoute(666), {}) 46 | const store = mockStore() 47 | return store.dispatch(sayHelloAsync(666)) 48 | .then(() => { 49 | expect(store.getActions()).toEqual([ 50 | sayHelloAsyncRequest(), 51 | sayHelloAsyncFailure(), 52 | ]) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/src/client/index.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'babel-polyfill' 4 | 5 | import Immutable from 'immutable' 6 | import React from 'react' 7 | import ReactDOM from 'react-dom' 8 | import { AppContainer } from 'react-hot-loader' 9 | import { Provider } from 'react-redux' 10 | import { BrowserRouter } from 'react-router-dom' 11 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux' 12 | import thunkMiddleware from 'redux-thunk' 13 | 14 | import App from '../shared/app' 15 | import helloReducer from '../shared/reducer/hello' 16 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 17 | import { isProd } from '../shared/util' 18 | 19 | /* eslint-disable no-underscore-dangle */ 20 | const composeEnhancers = (isProd ? null : window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose 21 | const preloadedState = window.__PRELOADED_STATE__ 22 | /* eslint-enable no-underscore-dangle */ 23 | 24 | const store = createStore(combineReducers( 25 | { hello: helloReducer }), 26 | { hello: Immutable.fromJS(preloadedState.hello) }, 27 | composeEnhancers(applyMiddleware(thunkMiddleware))) 28 | 29 | const rootEl = document.querySelector(APP_CONTAINER_SELECTOR) 30 | 31 | const wrapApp = (AppComponent, reduxStore) => 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ReactDOM.render(wrapApp(App, store), rootEl) 41 | 42 | if (module.hot) { 43 | // flow-disable-next-line 44 | module.hot.accept('../shared/app', () => { 45 | // eslint-disable-next-line global-require 46 | const NextApp = require('../shared/app').default 47 | ReactDOM.render(wrapApp(NextApp, store), rootEl) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /07-socket-io/src/client/index.jsx: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import 'babel-polyfill' 4 | 5 | import Immutable from 'immutable' 6 | import React from 'react' 7 | import ReactDOM from 'react-dom' 8 | import { AppContainer } from 'react-hot-loader' 9 | import { Provider } from 'react-redux' 10 | import { BrowserRouter } from 'react-router-dom' 11 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux' 12 | import thunkMiddleware from 'redux-thunk' 13 | 14 | import setUpSocket from './socket' 15 | import App from '../shared/app' 16 | import helloReducer from '../shared/reducer/hello' 17 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 18 | import { isProd } from '../shared/util' 19 | 20 | /* eslint-disable no-underscore-dangle */ 21 | const composeEnhancers = (isProd ? null : window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose 22 | const preloadedState = window.__PRELOADED_STATE__ 23 | /* eslint-enable no-underscore-dangle */ 24 | 25 | const store = createStore(combineReducers( 26 | { hello: helloReducer }), 27 | { hello: Immutable.fromJS(preloadedState.hello) }, 28 | composeEnhancers(applyMiddleware(thunkMiddleware))) 29 | 30 | const rootEl = document.querySelector(APP_CONTAINER_SELECTOR) 31 | 32 | const wrapApp = (AppComponent, reduxStore) => 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ReactDOM.render(wrapApp(App, store), rootEl) 42 | 43 | if (module.hot) { 44 | // flow-disable-next-line 45 | module.hot.accept('../shared/app', () => { 46 | // eslint-disable-next-line global-require 47 | const NextApp = require('../shared/app').default 48 | ReactDOM.render(wrapApp(NextApp, store), rootEl) 49 | }) 50 | } 51 | 52 | setUpSocket(store) 53 | -------------------------------------------------------------------------------- /04-webpack-react-hmr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "yarn dev:start", 8 | "dev:start": "nodemon -e js,jsx --ignore lib --ignore dist --exec babel-node src/server", 9 | "dev:wds": "webpack-dev-server --progress", 10 | "prod:build": "rimraf lib dist && babel src -d lib --ignore .test.js && cross-env NODE_ENV=production webpack -p --progress", 11 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 12 | "prod:stop": "pm2 delete server", 13 | "lint": "eslint src webpack.config.babel.js --ext .js,.jsx", 14 | "test": "yarn lint && flow && jest --coverage", 15 | "precommit": "yarn test", 16 | "prepush": "yarn test && yarn prod:build" 17 | }, 18 | "dependencies": { 19 | "babel-polyfill": "^6.23.0", 20 | "compression": "^1.6.2", 21 | "express": "^4.15.2", 22 | "react": "^15.4.2", 23 | "react-dom": "^15.4.2", 24 | "react-hot-loader": "^3.0.0-beta.6" 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.24.0", 28 | "babel-core": "^6.24.0", 29 | "babel-eslint": "^7.1.1", 30 | "babel-jest": "^19.0.0", 31 | "babel-loader": "^6.4.0", 32 | "babel-plugin-flow-react-proptypes": "^0.21.0", 33 | "babel-preset-env": "^1.2.1", 34 | "babel-preset-flow": "^6.23.0", 35 | "babel-preset-react": "^6.23.0", 36 | "cross-env": "^3.2.3", 37 | "eslint": "^3.17.1", 38 | "eslint-config-airbnb": "^14.1.0", 39 | "eslint-plugin-compat": "^1.0.2", 40 | "eslint-plugin-flowtype": "^2.30.3", 41 | "eslint-plugin-import": "^2.2.0", 42 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 43 | "eslint-plugin-react": "^6.9.0", 44 | "flow-bin": "^0.41.0", 45 | "husky": "^0.13.2", 46 | "jest": "^19.0.2", 47 | "nodemon": "^1.11.0", 48 | "pm2": "^2.4.2", 49 | "rimraf": "^2.6.1", 50 | "webpack": "^2.2.1", 51 | "webpack-dev-server": "^2.4.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /05-redux-immutable-fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "yarn dev:start", 8 | "dev:start": "nodemon -e js,jsx --ignore lib --ignore dist --exec babel-node src/server", 9 | "dev:wds": "webpack-dev-server --progress", 10 | "prod:build": "rimraf lib dist && babel src -d lib --ignore .test.js && cross-env NODE_ENV=production webpack -p --progress", 11 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 12 | "prod:stop": "pm2 delete server", 13 | "lint": "eslint src webpack.config.babel.js --ext .js,.jsx", 14 | "test": "yarn lint && flow && jest --coverage", 15 | "precommit": "yarn test", 16 | "prepush": "yarn test && yarn prod:build" 17 | }, 18 | "dependencies": { 19 | "babel-polyfill": "^6.23.0", 20 | "compression": "^1.6.2", 21 | "express": "^4.15.2", 22 | "immutable": "4.0.0-rc.2", 23 | "isomorphic-fetch": "^2.2.1", 24 | "react": "^15.4.2", 25 | "react-dom": "^15.4.2", 26 | "react-hot-loader": "^3.0.0-beta.6", 27 | "react-redux": "^5.0.3", 28 | "redux": "^3.6.0", 29 | "redux-actions": "^2.0.1", 30 | "redux-thunk": "^2.2.0" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.24.0", 34 | "babel-core": "^6.24.0", 35 | "babel-eslint": "^7.1.1", 36 | "babel-jest": "^19.0.0", 37 | "babel-loader": "^6.4.0", 38 | "babel-plugin-flow-react-proptypes": "^0.21.0", 39 | "babel-preset-env": "^1.2.1", 40 | "babel-preset-flow": "^6.23.0", 41 | "babel-preset-react": "^6.23.0", 42 | "cross-env": "^3.2.3", 43 | "eslint": "^3.17.1", 44 | "eslint-config-airbnb": "^14.1.0", 45 | "eslint-plugin-compat": "^1.0.2", 46 | "eslint-plugin-flowtype": "^2.30.3", 47 | "eslint-plugin-import": "^2.2.0", 48 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 49 | "eslint-plugin-react": "^6.9.0", 50 | "fetch-mock": "^5.9.4", 51 | "flow-bin": "^0.41.0", 52 | "husky": "^0.13.2", 53 | "jest": "^19.0.2", 54 | "nodemon": "^1.11.0", 55 | "pm2": "^2.4.2", 56 | "redux-mock-store": "^1.2.2", 57 | "rimraf": "^2.6.1", 58 | "webpack": "^2.2.1", 59 | "webpack-dev-server": "^2.4.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /06-react-router-ssr-helmet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "yarn dev:start", 8 | "dev:start": "nodemon -e js,jsx --ignore lib --ignore dist --exec babel-node src/server", 9 | "dev:wds": "webpack-dev-server --progress", 10 | "prod:build": "rimraf lib dist && babel src -d lib --ignore .test.js && cross-env NODE_ENV=production webpack -p --progress", 11 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 12 | "prod:stop": "pm2 delete server", 13 | "lint": "eslint src webpack.config.babel.js --ext .js,.jsx", 14 | "test": "yarn lint && flow && jest --coverage", 15 | "precommit": "yarn test", 16 | "prepush": "yarn test && yarn prod:build" 17 | }, 18 | "dependencies": { 19 | "babel-polyfill": "^6.23.0", 20 | "compression": "^1.6.2", 21 | "express": "^4.15.2", 22 | "immutable": "4.0.0-rc.2", 23 | "isomorphic-fetch": "^2.2.1", 24 | "react": "^15.4.2", 25 | "react-dom": "^15.4.2", 26 | "react-helmet": "^4.0.0", 27 | "react-hot-loader": "^3.0.0-beta.6", 28 | "react-redux": "^5.0.3", 29 | "react-router": "^4.0.0-beta.8", 30 | "react-router-dom": "^4.0.0-beta.8", 31 | "redux": "^3.6.0", 32 | "redux-actions": "^2.0.1", 33 | "redux-thunk": "^2.2.0" 34 | }, 35 | "devDependencies": { 36 | "babel-cli": "^6.24.0", 37 | "babel-core": "^6.24.0", 38 | "babel-eslint": "^7.1.1", 39 | "babel-jest": "^19.0.0", 40 | "babel-loader": "^6.4.0", 41 | "babel-plugin-flow-react-proptypes": "^0.21.0", 42 | "babel-preset-env": "^1.2.1", 43 | "babel-preset-flow": "^6.23.0", 44 | "babel-preset-react": "^6.23.0", 45 | "cross-env": "^3.2.3", 46 | "eslint": "^3.17.1", 47 | "eslint-config-airbnb": "^14.1.0", 48 | "eslint-plugin-compat": "^1.0.2", 49 | "eslint-plugin-flowtype": "^2.30.3", 50 | "eslint-plugin-import": "^2.2.0", 51 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 52 | "eslint-plugin-react": "^6.9.0", 53 | "fetch-mock": "^5.9.4", 54 | "flow-bin": "^0.41.0", 55 | "husky": "^0.13.2", 56 | "jest": "^19.0.2", 57 | "nodemon": "^1.11.0", 58 | "pm2": "^2.4.2", 59 | "redux-mock-store": "^1.2.2", 60 | "rimraf": "^2.6.1", 61 | "webpack": "^2.2.1", 62 | "webpack-dev-server": "^2.4.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /07-socket-io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-project", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "browserslist": ["> 1%"], 6 | "scripts": { 7 | "start": "yarn dev:start", 8 | "dev:start": "nodemon -e js,jsx --ignore lib --ignore dist --exec babel-node src/server", 9 | "dev:wds": "webpack-dev-server --progress", 10 | "prod:build": "rimraf lib dist && babel src -d lib --ignore .test.js && cross-env NODE_ENV=production webpack -p --progress", 11 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 12 | "prod:stop": "pm2 delete server", 13 | "lint": "eslint src webpack.config.babel.js --ext .js,.jsx", 14 | "test": "yarn lint && flow && jest --coverage", 15 | "precommit": "yarn test", 16 | "prepush": "yarn test && yarn prod:build" 17 | }, 18 | "dependencies": { 19 | "babel-polyfill": "^6.23.0", 20 | "compression": "^1.6.2", 21 | "express": "^4.15.2", 22 | "immutable": "4.0.0-rc.2", 23 | "isomorphic-fetch": "^2.2.1", 24 | "react": "^15.4.2", 25 | "react-dom": "^15.4.2", 26 | "react-helmet": "^4.0.0", 27 | "react-hot-loader": "^3.0.0-beta.6", 28 | "react-redux": "^5.0.3", 29 | "react-router": "^4.0.0-beta.8", 30 | "react-router-dom": "^4.0.0-beta.8", 31 | "redux": "^3.6.0", 32 | "redux-actions": "^2.0.1", 33 | "redux-thunk": "^2.2.0", 34 | "socket.io": "^1.7.3", 35 | "socket.io-client": "^1.7.3" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "^6.24.0", 39 | "babel-core": "^6.24.0", 40 | "babel-eslint": "^7.1.1", 41 | "babel-jest": "^19.0.0", 42 | "babel-loader": "^6.4.0", 43 | "babel-plugin-flow-react-proptypes": "^0.21.0", 44 | "babel-preset-env": "^1.2.1", 45 | "babel-preset-flow": "^6.23.0", 46 | "babel-preset-react": "^6.23.0", 47 | "cross-env": "^3.2.3", 48 | "eslint": "^3.17.1", 49 | "eslint-config-airbnb": "^14.1.0", 50 | "eslint-plugin-compat": "^1.0.2", 51 | "eslint-plugin-flowtype": "^2.30.3", 52 | "eslint-plugin-import": "^2.2.0", 53 | "eslint-plugin-jsx-a11y": "^3.0.2 || ^4.0.0", 54 | "eslint-plugin-react": "^6.9.0", 55 | "fetch-mock": "^5.9.4", 56 | "flow-bin": "^0.41.0", 57 | "husky": "^0.13.2", 58 | "jest": "^19.0.2", 59 | "nodemon": "^1.11.0", 60 | "pm2": "^2.4.2", 61 | "redux-mock-store": "^1.2.2", 62 | "rimraf": "^2.6.1", 63 | "webpack": "^2.2.1", 64 | "webpack-dev-server": "^2.4.1" 65 | } 66 | } 67 | --------------------------------------------------------------------------------