├── .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 | [](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 |
23 |
24 | } />
25 | } />
26 | } />
27 |
28 |
29 |
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 |
23 |
24 | } />
25 | } />
26 | } />
27 |
28 |
29 |
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 |
--------------------------------------------------------------------------------