├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── .eslintignore ├── .gitattributes ├── src ├── routes │ ├── HelloWorld │ │ ├── constants │ │ │ └── index.js │ │ ├── controller │ │ │ ├── action.js │ │ │ └── reducer.js │ │ └── index.js │ ├── StartCoding │ │ ├── constants │ │ │ └── index.js │ │ ├── controller │ │ │ ├── action.js │ │ │ └── reducer.js │ │ └── index.js │ └── index.js ├── .DS_Store ├── favicon.ico ├── controller │ ├── history.js │ ├── initialState.js │ ├── actions.js │ ├── middleware │ │ ├── index.js │ │ ├── reduxLogger.js │ │ └── rootReducer.js │ ├── reducers.js │ └── store.js ├── components │ ├── index.js │ ├── Footer.js │ ├── LoadingPlaceholder.js │ ├── Header.js │ └── Body.js ├── manifest.json ├── index.html ├── layout │ └── index.js ├── container │ └── index.js ├── index.js └── styles │ └── index.scss ├── .DS_Store ├── lerna.json ├── postcss.config.js ├── .gitignore ├── prettier.config.js ├── globals.js ├── .babelrc ├── setupTests.js ├── tsconfig.json ├── jest.config.js ├── .editorconfig ├── LICENSE ├── typings.d.ts ├── server ├── compiler.js └── server.js ├── package.json ├── webpack.config.js ├── .stylelintrc ├── tslint.json └── .eslintrc /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Redux-Suspense-Lazy-Memo-test 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/** 2 | node_modules/** 3 | dist/** 4 | src/index.html 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /src/routes/HelloWorld/constants/index.js: -------------------------------------------------------------------------------- 1 | export const COUNT_ADD = 'HelloWorld/COUNT_ADD'; 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiosBoy/React-Redux-Suspense-Lazy-Memo-test/HEAD/.DS_Store -------------------------------------------------------------------------------- /src/routes/StartCoding/constants/index.js: -------------------------------------------------------------------------------- 1 | export const COUNT_ADD = 'StartCoding/COUNT_ADD'; 2 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiosBoy/React-Redux-Suspense-Lazy-Memo-test/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BiosBoy/React-Redux-Suspense-Lazy-Memo-test/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.11.0", 3 | "npmClient": "yarn", 4 | "useWorkspaces": false, 5 | "hoist": true 6 | } 7 | -------------------------------------------------------------------------------- /src/controller/history.js: -------------------------------------------------------------------------------- 1 | import { createBrowserHistory } from 'history'; 2 | 3 | const history = createBrowserHistory(); 4 | 5 | export default history; 6 | -------------------------------------------------------------------------------- /src/controller/initialState.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | common: { 3 | appName: 'react-suspense' 4 | } 5 | }; 6 | 7 | export default initialState; 8 | -------------------------------------------------------------------------------- /src/controller/actions.js: -------------------------------------------------------------------------------- 1 | import { COUNT_ADD } from '../constants'; 2 | 3 | const addCount = count => ({ 4 | type: COUNT_ADD, 5 | count 6 | }); 7 | 8 | export { addCount }; 9 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'postcss-cssnext': {}, 5 | 'postcss-preset-env': {}, 6 | 'cssnano': {} 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/controller/middleware/index.js: -------------------------------------------------------------------------------- 1 | import logger from './reduxLogger'; 2 | import makeRootReducer, { injectReducer } from './rootReducer'; 3 | 4 | export { logger, makeRootReducer, injectReducer }; 5 | -------------------------------------------------------------------------------- /src/routes/HelloWorld/controller/action.js: -------------------------------------------------------------------------------- 1 | import { COUNT_ADD } from '../constants'; 2 | 3 | const addCount = count => ({ 4 | type: COUNT_ADD, 5 | count 6 | }); 7 | 8 | export { addCount }; 9 | -------------------------------------------------------------------------------- /src/routes/StartCoding/controller/action.js: -------------------------------------------------------------------------------- 1 | import { COUNT_ADD } from '../constants'; 2 | 3 | const addCount = count => ({ 4 | type: COUNT_ADD, 5 | count 6 | }); 7 | 8 | export { addCount }; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | // put here all file that you need to execlude from pushing on git platform. 2 | 3 | *.log 4 | node_modules 5 | dist 6 | coverage 7 | .idea 8 | package-lock.json 9 | .vscode 10 | .tmp 11 | .src 12 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | useTabs: false, 3 | printWidth: 120, 4 | tabWidth: 2, 5 | singleQuote: true, 6 | trailingComma: 'none', 7 | jsxBracketSameLine: false, 8 | semi: false 9 | }; 10 | -------------------------------------------------------------------------------- /src/controller/middleware/reduxLogger.js: -------------------------------------------------------------------------------- 1 | import { createLogger } from 'redux-logger'; 2 | 3 | const logger = createLogger({ 4 | collapsed: true, 5 | timestamp: false, 6 | diff: true 7 | }); 8 | 9 | export default logger; 10 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | import Body from './Body'; 3 | import Footer from './Footer'; 4 | import LoadingPlaceholder from './LoadingPlaceholder'; 5 | 6 | export { Header, Body, Footer, LoadingPlaceholder }; 7 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from '../styles/index.scss'; 3 | 4 | const Footer = () => { 5 | return
App Footer
; 6 | }; 7 | 8 | export default memo(Footer); 9 | -------------------------------------------------------------------------------- /globals.js: -------------------------------------------------------------------------------- 1 | // global variables for webpack and app hot-reload. Make them work more easly. 2 | global.__TEST__ = process.env.NODE_ENV === 'test'; 3 | global.__DEV__ = process.env.NODE_ENV === 'development'; 4 | global.__PROD__ = process.env.NODE_ENV === 'production'; 5 | global.__NODE_ENV__ = process.env.NODE_ENV; 6 | global.__PORT__ = process.env.PORT; 7 | -------------------------------------------------------------------------------- /src/components/LoadingPlaceholder.js: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from '../styles/index.scss'; 3 | 4 | const LoadingPlaceholder = () => { 5 | return ( 6 |
7 | Loading... 8 |
9 | ); 10 | }; 11 | 12 | export default memo(LoadingPlaceholder); 13 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Templater", 3 | "name": "Webpack4-React17 Templater", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/env", { 4 | "targets": { 5 | "browsers": ["last 2 versions"] 6 | } 7 | }], 8 | "@babel/react", 9 | "@babel/typescript" 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-proposal-class-properties", 13 | "@babel/plugin-syntax-dynamic-import", 14 | ["@babel/plugin-transform-async-to-generator", { 15 | "module": "bluebird", 16 | "method": "coroutine" 17 | }] 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, memo } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import styles from '../styles/index.scss'; 4 | 5 | const Header = () => { 6 | return ( 7 | 8 |
9 |
10 | Home 11 | Next 12 |
13 | App Headers 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default memo(Header); 20 | -------------------------------------------------------------------------------- /setupTests.js: -------------------------------------------------------------------------------- 1 | // TODO: Remove these polyfills once the below issue is solved. 2 | // It present here to allow Jest work with the last React environment. 3 | // https://github.com/facebookincubator/create-react-app/issues/3199#issuecomment-332842582 4 | global.requestAnimationFrame = cb => { 5 | setTimeout(cb, 0); 6 | }; 7 | 8 | global.matchMedia = window.matchMedia || function() { 9 | return { 10 | matches: false, 11 | addListener: () => {}, 12 | removeListener: () => {} 13 | }; 14 | }; 15 | 16 | import Enzyme from 'enzyme'; 17 | import Adapter from 'enzyme-adapter-react-16'; 18 | 19 | Enzyme.configure({ adapter: new Adapter() }); 20 | -------------------------------------------------------------------------------- /src/controller/reducers.js: -------------------------------------------------------------------------------- 1 | import { LOCATION_CHANGE } from 'connected-react-router'; 2 | import initialState from './initialState'; 3 | 4 | // ------------------------------------ 5 | // Action Handlers 6 | // ------------------------------------ 7 | const ACTION_HANDLERS = { 8 | [LOCATION_CHANGE]: (state, action) => ({ 9 | ...state, 10 | locationChange: action.payload.location.pathname 11 | }) 12 | }; 13 | 14 | // ------------------------------------ 15 | // Reducer 16 | // ------------------------------------ 17 | const reducer = (state = initialState, action) => { 18 | const handler = ACTION_HANDLERS[action.type]; 19 | 20 | return handler ? handler(state, action) : state; 21 | }; 22 | 23 | export default reducer; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noUnusedLocals": true, 4 | "noUnusedParameters": true, 5 | "allowSyntheticDefaultImports": true, 6 | "esModuleInterop": true, 7 | "allowJs": true, 8 | "checkJs": false, 9 | "module": "esnext", 10 | "target": "es5", 11 | "jsx": "react", 12 | "moduleResolution": "node", 13 | "lib": ["es6", "dom"] 14 | }, 15 | "typeAcquisition": { 16 | "enable": true 17 | }, 18 | "typeRoots": [ 19 | "./typings.d.ts", 20 | "./node_modules/@types" 21 | ], 22 | "include": [ 23 | ".src/.ts", 24 | "src/**/*", 25 | "./typings.d.ts" 26 | ], 27 | "exclude": [ 28 | "node_modules", 29 | "**/*.test.ts", 30 | "server", 31 | "dist" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Suspense + lazy React 16.7 Testing stent 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // Jest tesitng config. Responce for app tests. 2 | module.exports = { 3 | cacheDirectory: '/.tmp/jest', 4 | coverageDirectory: './.tmp/coverage', 5 | moduleNameMapper: { 6 | '^.+\\.(scss)$': 'identity-obj-proxy' 7 | }, 8 | modulePaths: [''], 9 | moduleFileExtensions: ['js', 'jsx', 'json'], 10 | globals: { 11 | NODE_ENV: 'test' 12 | }, 13 | verbose: true, 14 | testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$', 15 | testPathIgnorePatterns: ['/node_modules/', '/__tests__/mocks/.*'], 16 | transformIgnorePatterns: ['.*(node_modules)(?!.*torn.*).*$'], 17 | transform: { 18 | '^.+\\.js$': 'babel-jest' 19 | }, 20 | setupFiles: ['/setupTests.js'], 21 | snapshotSerializers: ['enzyme-to-json/serializer'] 22 | }; 23 | -------------------------------------------------------------------------------- /src/routes/StartCoding/controller/reducer.js: -------------------------------------------------------------------------------- 1 | import { COUNT_ADD } from '../constants'; 2 | 3 | // ------------------------------------ 4 | // Action Handlers 5 | // ------------------------------------ 6 | const ACTION_HANDLERS = { 7 | [COUNT_ADD]: (state, action) => ({ 8 | ...state, 9 | count: action.count, 10 | countDoubl: action.count % 2 === 0 ? action.count : state.countDoubl 11 | }) 12 | }; 13 | 14 | const initialState = { 15 | count: 5, 16 | countDoubl: 2 17 | }; 18 | 19 | // ------------------------------------ 20 | // Reducer 21 | // ------------------------------------ 22 | const reducer = (state = initialState, action) => { 23 | const handler = ACTION_HANDLERS[action.type]; 24 | 25 | return handler ? handler(state, action) : state; 26 | }; 27 | 28 | export default reducer; 29 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React, { lazy } from 'react'; 2 | import { injectReducer } from '../controller/middleware'; 3 | import rootStore from '../controller/store'; 4 | 5 | const HelloWorld = lazy(() => import(/* webpackChunkName: "HelloWorld" */ './HelloWorld')); 6 | const StartCoding = lazy(() => import(/* webpackChunkName: "StartCoding" */ './StartCoding')); 7 | 8 | const Components = { 9 | HelloWorld, 10 | StartCoding 11 | }; 12 | 13 | const AsyncComponent = props => { 14 | const { componentName } = props; 15 | 16 | import(`./${componentName}/controller/reducer`).then(({ default: reducer }) => { 17 | injectReducer(rootStore, { key: componentName, reducer }); 18 | }); 19 | 20 | const Component = Components[componentName]; 21 | 22 | return ; 23 | }; 24 | 25 | export default AsyncComponent; 26 | -------------------------------------------------------------------------------- /src/routes/HelloWorld/controller/reducer.js: -------------------------------------------------------------------------------- 1 | import { COUNT_ADD } from '../constants'; 2 | 3 | // ------------------------------------ 4 | // Action Handlers 5 | // ------------------------------------ 6 | const ACTION_HANDLERS = { 7 | [COUNT_ADD]: (state, action) => ({ 8 | ...state, 9 | count: action.count, 10 | countDoubl: action.count % 2 === 0 ? action.count : state.countDoubl 11 | }) 12 | }; 13 | 14 | const initialState = { 15 | count: 5, 16 | countDoubl: 2 17 | }; 18 | 19 | // ------------------------------------ 20 | // Reducer 21 | // ------------------------------------ 22 | const reducer = (state = initialState, action) => { 23 | const handler = ACTION_HANDLERS[action.type]; 24 | 25 | // console.log(state, action) 26 | return handler ? handler(state, action) : state; 27 | }; 28 | 29 | export default reducer; 30 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # A special property that should be specified at the top of the file outside of 4 | # any sections. Set to true to stop .editor config file search on current file 5 | root = true 6 | 7 | [*] 8 | # Indentation style 9 | # Possible values - tab, space 10 | indent_style = space 11 | 12 | # Indentation size in single-spaced characters 13 | # Possible values - an integer, tab 14 | indent_size = 2 15 | 16 | # Line ending file format 17 | # Possible values - lf, crlf, cr 18 | end_of_line = lf 19 | 20 | # File character encoding 21 | # Possible values - latin1, utf-8, utf-16be, utf-16le 22 | charset = utf-8 23 | 24 | # Denotes whether to trim whitespace at the end of lines 25 | # Possible values - true, false 26 | trim_trailing_whitespace = true 27 | 28 | # Denotes whether file should end with a newline 29 | # Possible values - true, false 30 | insert_final_newline = true 31 | -------------------------------------------------------------------------------- /src/controller/middleware/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { connectRouter } from 'connected-react-router'; 3 | import { createResponsiveStateReducer } from 'redux-responsive'; 4 | import common from '../reducers'; 5 | import history from '../history'; 6 | 7 | const makeRootReducer = asyncReducers => { 8 | return combineReducers({ 9 | ...asyncReducers, 10 | common, 11 | // routing 12 | router: connectRouter(history), 13 | browser: createResponsiveStateReducer({ 14 | mobile: 600, 15 | tablet: 1000, 16 | desktop: 5000 17 | }) 18 | }); 19 | }; 20 | 21 | export const injectReducer = (store, { key, reducer }) => { 22 | if (Object.hasOwnProperty.call(store.asyncReducers, key)) return; 23 | store.asyncReducers[key] = reducer; 24 | 25 | store.replaceReducer(makeRootReducer(store.asyncReducers)); 26 | }; 27 | 28 | export default makeRootReducer; 29 | -------------------------------------------------------------------------------- /src/controller/store.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, compose, createStore } from 'redux'; 2 | import { routerMiddleware } from 'connected-react-router'; 3 | 4 | import { logger } from './middleware'; 5 | import makeRootReducer from './middleware/rootReducer'; 6 | import initialState from './initialState'; 7 | import history from './history'; 8 | 9 | const rootStore = () => { 10 | const middleware = [routerMiddleware(history), logger]; 11 | const enhancers = []; 12 | 13 | if (__DEV__ && window.__REDUX_DEVTOOLS_EXTENSION__) { 14 | enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); 15 | } 16 | 17 | // ====================================================== 18 | // Store Instantiation and HMR Setup 19 | // ====================================================== 20 | const store = createStore( 21 | makeRootReducer(), 22 | initialState, 23 | compose( 24 | applyMiddleware(...middleware), 25 | ...enhancers 26 | ) 27 | ); 28 | 29 | store.asyncReducers = {}; 30 | 31 | return store; 32 | }; 33 | 34 | export default rootStore(); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 sviatoslav 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. -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any; 3 | __REDUX_DEVTOOLS_EXTENSION__: any; 4 | } 5 | 6 | declare const __DEV__: string; 7 | declare const __PROD__: string; 8 | declare const __TEST__: string; 9 | 10 | declare module '*.json' { 11 | interface IClassNames { 12 | [className: string]: string 13 | } 14 | const classNames: IClassNames; 15 | export = classNames; 16 | } 17 | 18 | declare module '*.css' { 19 | interface IClassNames { 20 | [className: string]: string 21 | } 22 | const classNames: IClassNames; 23 | export = classNames; 24 | } 25 | 26 | declare module '*.scss' { 27 | interface IClassNames { 28 | [className: string]: string 29 | } 30 | const classNames: IClassNames; 31 | export = classNames; 32 | } 33 | 34 | declare module '*.cssmodule.scss' { 35 | interface IClassNames { 36 | [className: string]: string 37 | } 38 | const classNames: IClassNames; 39 | export = classNames; 40 | } 41 | 42 | declare module '*.svg' { 43 | interface IClassNames { 44 | [className: string]: string 45 | } 46 | const classNames: IClassNames; 47 | export = classNames; 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Body.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { Fragment, PureComponent } from 'react'; 3 | 4 | class Body extends PureComponent { 5 | _handleClick = () => { 6 | const { onclick } = this.props; 7 | 8 | onclick(); 9 | } 10 | 11 | render() { 12 | const { buttonClass, descriptionClass, title, description, count } = this.props; 13 | 14 | return ( 15 | 16 | 20 |
{description}
21 |
22 | ); 23 | } 24 | } 25 | 26 | Body.propTypes = { 27 | buttonClass: PropTypes.string, 28 | descriptionClass: PropTypes.string, 29 | onclick: PropTypes.func, 30 | title: PropTypes.string, 31 | description: PropTypes.string, 32 | count: PropTypes.number 33 | }; 34 | 35 | Body.defaultProps = { 36 | buttonClass: '', 37 | descriptionClass: '', 38 | onclick: () => {}, 39 | title: '', 40 | description: '', 41 | count: 0 42 | }; 43 | 44 | export default Body; 45 | -------------------------------------------------------------------------------- /src/layout/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { withRouter } from 'react-router-dom'; 5 | import { Header, Footer } from '../components'; 6 | import styles from '../styles/index.scss'; 7 | 8 | class AppLayout extends Component { 9 | render() { 10 | const { countDoubl, children } = this.props; 11 | console.log(this.props.state.router) 12 | 13 | return ( 14 |
15 |
16 | {children} 17 |
18 |
19 | ); 20 | } 21 | } 22 | 23 | AppLayout.propTypes = { 24 | children: PropTypes.node.isRequired, 25 | count: PropTypes.number, 26 | countDoubl: PropTypes.number, 27 | startAddCount: PropTypes.func 28 | }; 29 | 30 | AppLayout.defaultProps = { 31 | count: 0, 32 | countDoubl: 0, 33 | startAddCount: () => {} 34 | }; 35 | 36 | const mapStateToProps = state => ({ 37 | state, 38 | count: state.count, 39 | countDoubl: state.countDoubl 40 | }); 41 | 42 | export default withRouter( 43 | connect( 44 | mapStateToProps, 45 | null 46 | )(AppLayout) 47 | ); 48 | -------------------------------------------------------------------------------- /src/container/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { Suspense } from 'react'; 3 | import { Provider } from 'react-redux'; 4 | import { HashRouter, Switch, Route } from 'react-router-dom'; 5 | import { ConnectedRouter } from 'connected-react-router'; 6 | 7 | import AppLayout from '../layout'; 8 | import AsyncComponent from '../routes'; 9 | import { LoadingPlaceholder } from '../components'; 10 | 11 | const AppContainer = ({ store, history }) => { 12 | return ( 13 | 14 | 15 | 16 | 17 | }> 18 | 19 | } /> 20 | } /> 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | AppContainer.propTypes = { 31 | store: PropTypes.object.isRequired, 32 | history: PropTypes.object.isRequired 33 | }; 34 | 35 | export default AppContainer; 36 | -------------------------------------------------------------------------------- /server/compiler.js: -------------------------------------------------------------------------------- 1 | // import global vars for a whole app 2 | require('../globals'); 3 | 4 | const debug = require('debug')('app:build:webpack-compiler'); 5 | const webpack = require('webpack'); 6 | const webpackConfig = require('../webpack.config.js'); 7 | 8 | // ----------------------------- 9 | // STARTING APP COMPILATION PROCESS 10 | // ----------------------------- 11 | function webpackCompiler() { 12 | return new Promise((resolve, reject) => { 13 | const compiler = webpack(webpackConfig); 14 | 15 | compiler.run((err, stats) => { 16 | if (err) { 17 | debug('Webpack compiler encountered a fatal error.', err); 18 | 19 | return reject(err); 20 | } 21 | 22 | const jsonStats = stats.toJson(); 23 | 24 | debug('Webpack compilation is completed.'); 25 | 26 | resolve(jsonStats); 27 | }); 28 | }); 29 | } 30 | 31 | // ----------------------------- 32 | // READING WEBPACK CONFIGURATION 33 | // ----------------------------- 34 | const compile = () => { 35 | debug('Starting compiler.'); 36 | 37 | return Promise.resolve() 38 | .then(() => webpackCompiler(webpackConfig)) 39 | .then(() => { 40 | debug('Compilation completed successfully.'); 41 | }) 42 | .catch(err => { 43 | debug('Compiler encountered an error.', err); 44 | 45 | process.exit(1); 46 | }); 47 | }; 48 | 49 | compile(); 50 | -------------------------------------------------------------------------------- /src/routes/HelloWorld/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { PureComponent } from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { addCount } from './controller/action'; 5 | import { Body } from '../../components'; 6 | import styles from '../../styles/index.scss'; 7 | 8 | class HelloWorld extends PureComponent { 9 | _addCount = () => { 10 | const { count, startAddCount } = this.props; 11 | 12 | startAddCount(count + 1); 13 | } 14 | 15 | render() { 16 | const { countDoubl } = this.props; 17 | 18 | return ( 19 | 27 | ); 28 | } 29 | } 30 | 31 | HelloWorld.propTypes = { 32 | count: PropTypes.number, 33 | countDoubl: PropTypes.number, 34 | startAddCount: PropTypes.func 35 | }; 36 | 37 | HelloWorld.defaultProps = { 38 | count: 0, 39 | countDoubl: 0, 40 | startAddCount: () => {} 41 | }; 42 | 43 | const mapStateToProps = state => ({ 44 | count: state.HelloWorld.count, 45 | countDoubl: state.HelloWorld.countDoubl 46 | }); 47 | 48 | const mapDispatchToProps = dispatch => ({ 49 | startAddCount: value => dispatch(addCount(value)) 50 | }); 51 | 52 | export default connect( 53 | mapStateToProps, 54 | mapDispatchToProps 55 | )(HelloWorld); 56 | -------------------------------------------------------------------------------- /src/routes/StartCoding/index.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'prop-types'; 2 | import React, { PureComponent } from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { addCount } from './controller/action'; 5 | import { Body } from '../../components'; 6 | import styles from '../../styles/index.scss'; 7 | 8 | class StartCoding extends PureComponent { 9 | _addCount = () => { 10 | const { count, startAddCount } = this.props; 11 | 12 | startAddCount(count + 1); 13 | } 14 | 15 | render() { 16 | const { countDoubl } = this.props; 17 | 18 | return ( 19 | 27 | ); 28 | } 29 | } 30 | 31 | StartCoding.propTypes = { 32 | count: PropTypes.number, 33 | countDoubl: PropTypes.number, 34 | startAddCount: PropTypes.func 35 | }; 36 | 37 | StartCoding.defaultProps = { 38 | count: 0, 39 | countDoubl: 0, 40 | startAddCount: () => {} 41 | }; 42 | 43 | const mapStateToProps = state => ({ 44 | count: state.StartCoding.count, 45 | countDoubl: state.StartCoding.countDoubl 46 | }); 47 | 48 | const mapDispatchToProps = dispatch => ({ 49 | startAddCount: value => dispatch(addCount(value)) 50 | }); 51 | 52 | export default connect( 53 | mapStateToProps, 54 | mapDispatchToProps 55 | )(StartCoding); 56 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import RedBox from 'redbox-react'; 4 | import AppContainer from './container'; 5 | import store from './controller/store'; 6 | import history from './controller/history'; 7 | 8 | const ENTRY_POINT = document.querySelector('#react-app-root'); 9 | 10 | // creating starting endpoint for app. 11 | const render = () => { 12 | ReactDOM.render(, ENTRY_POINT); 13 | }; 14 | 15 | // this will help us understand where the problem is located once app will fall. 16 | const renderError = error => { 17 | ReactDOM.render(, ENTRY_POINT); 18 | }; 19 | 20 | // This code is excluded from production bundle 21 | if (__DEV__) { 22 | window.state = store; 23 | // ======================================================== 24 | // DEVELOPMENT STAGE! HOT MODULE REPLACE ACTIVATION! 25 | // ======================================================== 26 | const devRender = () => { 27 | if (module.hot) { 28 | module.hot.accept('./container/index.js', () => render()); 29 | } 30 | 31 | render(); 32 | }; 33 | 34 | // Wrap render in try/catch 35 | try { 36 | devRender(); 37 | } catch (error) { 38 | console.error(error); 39 | renderError(error); 40 | } 41 | } else { 42 | // ======================================================== 43 | // PRODUCTION GO! 44 | // ======================================================== 45 | render(); 46 | } 47 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // import global vars for a whole app 2 | require('../globals'); 3 | 4 | const path = require('path'); 5 | const browserSync = require('browser-sync'); 6 | const historyApiFallback = require('connect-history-api-fallback'); 7 | const webpack = require('webpack'); 8 | const webpackDevMiddleware = require('webpack-dev-middleware'); 9 | const webpackHotMiddleware = require('webpack-hot-middleware'); 10 | const webpackConfig = require('../webpack.config.js'); 11 | const bundler = webpack(webpackConfig); 12 | 13 | // ======================================================== 14 | // WEBPACK MIDDLEWARE CONFIGURATION 15 | // ======================================================== 16 | const devMiddlewareOptions = { 17 | publicPath: webpackConfig.output.publicPath, 18 | hot: true, 19 | headers: { 'Access-Control-Allow-Origin': '*' } 20 | }; 21 | 22 | // ======================================================== 23 | // Server Configuration 24 | // ======================================================== 25 | browserSync({ 26 | open: false, 27 | ghostMode: { 28 | clicks: false, 29 | forms: false, 30 | scroll: true 31 | }, 32 | server: { 33 | baseDir: path.resolve(__dirname, '../src'), 34 | middleware: [ 35 | historyApiFallback(), 36 | webpackDevMiddleware(bundler, devMiddlewareOptions), 37 | webpackHotMiddleware(bundler) 38 | ] 39 | }, 40 | files: [ 41 | 'src/../*.tsx', 42 | 'src/../*.ts', 43 | 'src/../*.jsx', 44 | 'src/../*.js', 45 | 'src/../*.json', 46 | 'src/../*.scss', 47 | 'src/../*.html' 48 | ] 49 | }); 50 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | .appWrapper { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | width: 100%; 7 | height: auto; 8 | outline: none; 9 | } 10 | 11 | .extra { 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | width: 100%; 16 | height: 25vh; 17 | font-size: 25px; 18 | text-align: center; 19 | } 20 | 21 | .appHeader { 22 | composes: extra; 23 | color: wheat; 24 | background: green; 25 | height: 15vh; 26 | font-size: 15px; 27 | position: relative; 28 | 29 | .buttonsContainer { 30 | position: absolute; 31 | left: 0; 32 | } 33 | 34 | a { 35 | margin: 5px; 36 | color: black; 37 | } 38 | } 39 | 40 | .helloWorld { 41 | composes: extra; 42 | background: linear-gradient(#e66465, #9198e5); 43 | color: white; 44 | 45 | span { 46 | &:first-of-type { 47 | cursor: pointer; 48 | user-select: none; 49 | background: #1ea247; 50 | border: 3px solid #45ca24; 51 | border-radius: 12px; 52 | padding: 8px; 53 | margin: 5px; 54 | box-shadow: 0 5px 15px #000; 55 | } 56 | } 57 | } 58 | 59 | .helloStart { 60 | composes: extra; 61 | background: linear-gradient(#9198e5, #e66465 ); 62 | color: white; 63 | } 64 | 65 | .appFooter { 66 | composes: extra; 67 | background: yellow; 68 | height: 15vh; 69 | font-size: 15px; 70 | } 71 | 72 | .loadingPlaceholder { 73 | width: 100%; 74 | height: 50vh; 75 | display: flex; 76 | flex-direction: row; 77 | justify-content: center; 78 | align-items: center; 79 | top: 0; 80 | left: 0; 81 | background: black; 82 | 83 | span { 84 | font-size: 50px; 85 | color: white; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack4react17StarterKit", 3 | "version": "1.0.0", 4 | "description": "test stend to show how easy we can make code-splitting by in-box features in React 16.7 - Suspense and lazy.", 5 | "main": "index.js", 6 | "author": "Sviat Kuzhelev", 7 | "license": "MIT", 8 | "husky": { 9 | "hooks": { 10 | "pre-commit": "lint-staged" 11 | } 12 | }, 13 | "lint-staged": { 14 | "*.json": [ 15 | "jsonlint --formatter=verbose", 16 | "git add" 17 | ], 18 | "*.@(css|scss)": [ 19 | "stylelint --fix --formatter=verbose", 20 | "git add" 21 | ], 22 | "*.cssmodule": [ 23 | "stylelint --fix --syntax scss --formatter=verbose", 24 | "git add" 25 | ], 26 | "*.@(js|jsx)": [ 27 | "prettier --write", 28 | "eslint --fix --quiet", 29 | "git add", 30 | "jest --bail --findRelatedTests" 31 | ], 32 | "*.@(ts|tsx)": [ 33 | "prettier --write --parser typescript", 34 | "tslint --config tslint.json", 35 | "git add", 36 | "jest --bail --findRelatedTests" 37 | ] 38 | }, 39 | "scripts": { 40 | "start:dev": "better-npm-run start:dev", 41 | "start:prod": "better-npm-run start:prod", 42 | "test": "better-npm-run test", 43 | "clean": "rimraf dist", 44 | "push": "npm run lint && git push", 45 | "compile": "better-npm-run compile", 46 | "lint": "prettier --single-quote --no-semi --print-width 120 --write '{src,tests}/**/*.js'", 47 | "eslint": "eslint --quiet ../../.eslintrc", 48 | "csslint": "stylelint **/*.scss --config ../../.stylelintrc" 49 | }, 50 | "betterScripts": { 51 | "compile": { 52 | "command": "node server/compiler", 53 | "env": { 54 | "NODE_ENV": "production", 55 | "DEBUG": "app:*" 56 | } 57 | }, 58 | "start:dev": { 59 | "command": "node server/server", 60 | "env": { 61 | "NODE_ENV": "development", 62 | "DEBUG": "app:*" 63 | } 64 | }, 65 | "start:prod": { 66 | "command": "node server/server", 67 | "env": { 68 | "NODE_ENV": "production", 69 | "DEBUG": "app:*" 70 | } 71 | }, 72 | "test": { 73 | "command": "node server/server", 74 | "env": { 75 | "NODE_ENV": "test", 76 | "DEBUG": "app:*" 77 | } 78 | } 79 | }, 80 | "repository": { 81 | "type": "git" 82 | }, 83 | "devDependencies": { 84 | "@types/classnames": "^2.2.6", 85 | "@types/enzyme": "^3.1.14", 86 | "@types/enzyme-adapter-react-16": "^1.0.3", 87 | "@types/jest": "^23.3.9", 88 | "@types/lodash": "^4.14.117", 89 | "@types/node": "^10.12.1", 90 | "@types/prop-types": "^15.5.6", 91 | "@types/react-dom": "^16.0.9", 92 | "@types/react-redux": "^6.0.9", 93 | "@types/webpack": "^4.4.17", 94 | "@types/webpack-dev-middleware": "^2.0.2", 95 | "@types/webpack-env": "^1.13.6", 96 | "@types/webpack-hot-middleware": "^2.16.4", 97 | "autoprefixer": "^9.3.1", 98 | "babel-eslint": "^10.0.1", 99 | "better-npm-run": "^0.1.1", 100 | "classnames": "^2.2.6", 101 | "css-loader": "^1.0.1", 102 | "cssnano": "^4.1.7", 103 | "enzyme": "^3.7.0", 104 | "enzyme-to-json": "^3.3.4", 105 | "eslint": "^5.8.0", 106 | "eslint-config-airbnb": "^17.1.0", 107 | "eslint-plugin-babel": "^5.0.0", 108 | "eslint-plugin-import": "^2.11.0", 109 | "eslint-plugin-jest": "^21.22.0", 110 | "eslint-plugin-jsx-a11y": "^6.1.1", 111 | "eslint-plugin-promise": "^3.4.0", 112 | "eslint-plugin-react": "^7.11.1", 113 | "eslint-plugin-react-hooks": "^0.0.0", 114 | "husky": "^1.1.2", 115 | "jsonlint": "^1.6.3", 116 | "lerna": "^2.11.0", 117 | "lint-staged": "^8.0.3", 118 | "node-sass": "^4.9.4", 119 | "postcss-cssnext": "^3.1.0", 120 | "postcss-import": "^12.0.1", 121 | "postcss-loader": "^3.0.0", 122 | "postcss-preset-env": "^6.3.0", 123 | "postcss-scss": "^2.0.0", 124 | "precss": "^3.1.2", 125 | "prettier": "^1.14.3", 126 | "redbox-react": "^1.6.0", 127 | "redux-responsive": "^4.3.8", 128 | "sass-loader": "^7.1.0", 129 | "style-loader": "^0.23.1", 130 | "stylelint": "^9.7.1", 131 | "stylelint-config-sass-guidelines": "^5.2.0", 132 | "stylelint-config-standard": "^18.2.0", 133 | "stylelint-no-unsupported-browser-features": "^3.0.1", 134 | "stylelint-order": "^1.0.0", 135 | "stylelint-scss": "^3.3.0", 136 | "tslint": "^5.11.0", 137 | "url-loader": "^1.1.2", 138 | "webpack-bundle-analyzer": "^3.0.3" 139 | }, 140 | "dependencies": { 141 | "@babel/core": "^7.1.5", 142 | "@babel/plugin-proposal-class-properties": "^7.1.0", 143 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 144 | "@babel/plugin-transform-async-to-generator": "^7.1.0", 145 | "@babel/polyfill": "^7.0.0", 146 | "@babel/preset-env": "^7.1.0", 147 | "@babel/preset-react": "^7.0.0", 148 | "@babel/preset-stage-3": "^7.0.0", 149 | "@babel/preset-typescript": "^7.1.0", 150 | "babel": "^6.23.0", 151 | "babel-loader": "^8.0.4", 152 | "babel-plugin-transform-runtime": "^6.23.0", 153 | "babel-polyfill": "^6.26.0", 154 | "babel-preset-es2015": "^6.24.1", 155 | "babel-preset-stage-0": "^6.24.1", 156 | "browser-sync": "^2.26.3", 157 | "browser-sync-webpack-plugin": "^2.2.2", 158 | "clean-webpack-plugin": "^0.1.19", 159 | "connect-history-api-fallback": "^1.5.0", 160 | "connected-react-router": "^5.0.1", 161 | "debug": "^4.1.0", 162 | "history": "^4.7.2", 163 | "html-loader": "^0.5.5", 164 | "html-webpack-plugin": "^3.2.0", 165 | "mini-css-extract-plugin": "^0.4.1", 166 | "optimize-css-assets-webpack-plugin": "^5.0.1", 167 | "path": "^0.12.7", 168 | "prop-types": "^15.6.0", 169 | "react": "^16.7.0-alpha.0", 170 | "react-dom": "^16.7.0-alpha.0", 171 | "react-redux": "^5.1.1", 172 | "react-router": "^4.3.1", 173 | "react-router-dom": "^4.3.1", 174 | "redux": "^4.0.1", 175 | "redux-logger": "^3.0.6", 176 | "regenerator-runtime": "^0.12.1", 177 | "uglifyjs-webpack-plugin": "^1.2.7", 178 | "webpack": "^4.23.1", 179 | "webpack-cli": "^3.1.2", 180 | "webpack-dev-middleware": "^3.4.0", 181 | "webpack-dev-server": "^3.1.10", 182 | "webpack-hot-middleware": "^2.24.3" 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | // import global vars for a whole app 3 | require('./globals'); 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 8 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 9 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 10 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 11 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); 12 | const debug = require('debug')('app:webpack:config'); 13 | 14 | // ------------------------------------ 15 | // RULES INJECTION! 16 | // ------------------------------------ 17 | const rules = [ 18 | // JAVASCRIPT/JSON 19 | { 20 | test: /\.(js|jsx|ts|tsx)?$/, 21 | use: ['babel-loader'] 22 | }, 23 | { 24 | type: 'javascript/auto', 25 | test: /\.json$/, 26 | loader: 'json-loader' 27 | }, 28 | // STYLES 29 | { 30 | test: /.scss$/, 31 | use: [ 32 | __PROD__ ? MiniCssExtractPlugin.loader : 'style-loader', 33 | 'css-loader?modules&importLoaders=1&localIdentName=[local]___[hash:base64:5]', 34 | { loader: 'postcss-loader' }, 35 | 'sass-loader' 36 | ] 37 | }, 38 | // FILE/IMAGES 39 | { 40 | test: /\.woff(\?.*)?$/, 41 | loader: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff' 42 | }, 43 | { 44 | test: /\.woff2(\?.*)?$/, 45 | loader: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2' 46 | }, 47 | { 48 | test: /\.otf(\?.*)?$/, 49 | loader: 'file-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype' 50 | }, 51 | { 52 | test: /\.ttf(\?.*)?$/, 53 | loader: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream' 54 | }, 55 | { 56 | test: /\.eot(\?.*)?$/, 57 | loader: 'file-loader?prefix=fonts/&name=[path][name].[ext]' 58 | }, 59 | { 60 | test: /\.svg(\?.*)?$/, 61 | loader: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' 62 | }, 63 | { 64 | test: /\.(png|jpg)$/, 65 | loader: 'url-loader?limit=8192' 66 | } 67 | ]; 68 | 69 | // ------------------------------------ 70 | // BUNDLES OPTIMIZATION 71 | // ------------------------------------ 72 | const optimization = { 73 | optimization: { 74 | splitChunks: { 75 | chunks: 'all', 76 | minChunks: 2 77 | }, 78 | minimizer: [ 79 | new UglifyJsPlugin({ 80 | uglifyOptions: { 81 | compress: { 82 | unused: true, 83 | dead_code: true, 84 | warnings: false 85 | } 86 | }, 87 | sourceMap: true 88 | }), 89 | new OptimizeCSSAssetsPlugin({}) 90 | ] 91 | }, 92 | performance: { 93 | hints: false 94 | } 95 | }; 96 | 97 | // ------------------------------------ 98 | // STAGE PLUGINS INJECTION! [DEVELOPMENT, PRODUCTION, TESTING] 99 | // ------------------------------------ 100 | const stagePlugins = { 101 | test: [ 102 | new BundleAnalyzerPlugin() 103 | ], 104 | development: [ 105 | new HtmlWebpackPlugin({ 106 | template: path.resolve('./src/index.html'), 107 | filename: 'index.html', 108 | inject: 'body', 109 | minify: false, 110 | chunksSortMode: 'auto' 111 | }), 112 | new webpack.HotModuleReplacementPlugin(), 113 | new webpack.NoEmitOnErrorsPlugin() 114 | ], 115 | production: [ 116 | new MiniCssExtractPlugin({ 117 | filename: '[name].[hash].css', 118 | chunkFilename: '[name].[hash].css' 119 | }), 120 | new HtmlWebpackPlugin({ 121 | template: path.resolve('./src/index.html'), 122 | filename: 'index.html', 123 | inject: 'body', 124 | minify: { 125 | collapseWhitespace: true 126 | }, 127 | chunksSortMode: 'auto' 128 | }), 129 | new webpack.ProvidePlugin({ 130 | fetch: 'exports-loader?self.fetch!whatwg-fetch' 131 | }) 132 | ] 133 | }; 134 | 135 | // ------------------------------------ 136 | // STAGE CONFIGURATION INJECTION! [DEVELOPMENT, PRODUCTION] 137 | // ------------------------------------ 138 | const stageConfig = { 139 | development: { 140 | devtool: '', 141 | stats: { 142 | chunks: false, 143 | children: false, 144 | chunkModules: false, 145 | colors: true 146 | } 147 | }, 148 | production: { 149 | devtool: 'source-map', 150 | stats: { 151 | chunks: true, 152 | chunkModules: true, 153 | colors: true 154 | } 155 | } 156 | }; 157 | 158 | const createConfig = () => { 159 | debug('Creating configuration.'); 160 | debug(`Enabling devtools for "${__NODE_ENV__} Mode!"`); 161 | 162 | const webpackConfig = { 163 | mode: __DEV__ ? 'development' : 'production', 164 | name: 'client', 165 | target: 'web', 166 | devtool: stageConfig[__NODE_ENV__].devtool, 167 | stats: stageConfig[__NODE_ENV__].stats, 168 | module: { 169 | rules: [ 170 | ...rules 171 | ] 172 | }, 173 | ...optimization, 174 | resolve: { 175 | modules: ['node_modules'], 176 | extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] 177 | } 178 | }; 179 | 180 | // ------------------------------------ 181 | // Entry Points 182 | // ------------------------------------ 183 | webpackConfig.entry = { 184 | app: ['babel-polyfill', path.resolve(__dirname, 'src/index.js')].concat('webpack-hot-middleware/client?path=/__webpack_hmr') 185 | }; 186 | 187 | // ------------------------------------ 188 | // Bundle externals 189 | // ------------------------------------ 190 | webpackConfig.externals = { 191 | react: 'React', 192 | 'react-dom': 'ReactDOM' 193 | }; 194 | 195 | // ------------------------------------ 196 | // Bundle Output 197 | // ------------------------------------ 198 | webpackConfig.output = { 199 | filename: '[name].[hash].js', 200 | chunkFilename: '[name].[hash].js', 201 | path: path.resolve(__dirname, 'dist'), 202 | publicPath: '/' 203 | }; 204 | 205 | // ------------------------------------ 206 | // Plugins 207 | // ------------------------------------ 208 | debug(`Enable plugins for "${__NODE_ENV__} Mode!"`); 209 | webpackConfig.plugins = [ 210 | new webpack.DefinePlugin({ 211 | __DEV__, 212 | __PROD__, 213 | __TEST__ 214 | }), 215 | ...stagePlugins[__NODE_ENV__] 216 | ]; 217 | 218 | // ------------------------------------ 219 | // Finishing the Webpack configuration! 220 | // ------------------------------------ 221 | debug(`Webpack Bundles is Ready for "${__NODE_ENV__} Mode!"`); 222 | return webpackConfig; 223 | }; 224 | 225 | module.exports = createConfig(); 226 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "plugins": [ 4 | "stylelint-no-unsupported-browser-features", 5 | "stylelint-order" 6 | ], 7 | "ignoreFiles": ["**/*.js","**/*.jsx","**/*.ts","**/*.tsx","**/*.html"], 8 | "rules": { 9 | "declaration-block-no-redundant-longhand-properties": [ 10 | true, 11 | { 12 | "ignoreShorthands": ["flex-flow"] 13 | } 14 | ], 15 | "function-comma-space-after": "always", 16 | "declaration-block-no-duplicate-properties": null, 17 | "comment-empty-line-before": [ 18 | "always" 19 | ], 20 | "at-rule-no-unknown": [true, { 21 | "ignoreAtRules": ["function", "if", "each", "include", "mixin"] 22 | }], 23 | "at-rule-empty-line-before": [ 24 | "always", 25 | { 26 | "except": [ 27 | "after-same-name" 28 | ] 29 | } 30 | ], 31 | "number-leading-zero": "never", 32 | "indentation": [ 33 | 2, { 34 | "indentInsideParens": "once-at-root-twice-in-block", 35 | "severity": "error" 36 | } 37 | ], 38 | "plugin/no-unsupported-browser-features": [ 39 | true, 40 | { 41 | "severity": "warning", 42 | "ignore": [ 43 | "css-boxshadow", 44 | "pointer-events", 45 | "border-radius", 46 | "border-image", 47 | "css-gradients", 48 | "css-textshadow", 49 | "transforms2d", 50 | "css-animation", 51 | "css-transitions", 52 | "css-transform", 53 | "transition", 54 | "transform", 55 | "css-image-set", 56 | "flexbox", 57 | "viewport-units", 58 | "calc", 59 | "intrinsic-width" 60 | ] 61 | } 62 | ], 63 | "selector-pseudo-class-no-unknown": [ 64 | true, 65 | { 66 | "ignorePseudoClasses": ["global", "local"] 67 | } 68 | ], 69 | "selector-pseudo-class-case": null, 70 | "property-no-unknown": [ 71 | true, 72 | { 73 | "ignoreProperties": ["composes", "r"] 74 | } 75 | ], 76 | "unit-no-unknown": [ 77 | true, 78 | { 79 | "ignoreUnits": ["x"] 80 | } 81 | ], 82 | "order/order": [ 83 | [ 84 | "custom-properties", 85 | "at-variables", 86 | "declarations", 87 | "at-rules", 88 | "rules", 89 | "less-mixins" 90 | ], 91 | { 92 | "severity": "warning" 93 | } 94 | ], 95 | "order/properties-order": [ 96 | [ 97 | { 98 | "emptyLineBefore": "never", 99 | "properties": [ 100 | "content" 101 | ] 102 | }, 103 | { 104 | "emptyLineBefore": "never", 105 | "properties": [ 106 | "position", 107 | "top", 108 | "right", 109 | "bottom", 110 | "left", 111 | "z-index" 112 | ] 113 | }, 114 | { 115 | "emptyLineBefore": "never", 116 | "properties": [ 117 | "box-sizing", 118 | "display", 119 | "flex", 120 | "flex-basis", 121 | "flex-direction", 122 | "flex-flow", 123 | "flex-grow", 124 | "flex-shrink", 125 | "flex-wrap", 126 | "align-content", 127 | "align-items", 128 | "align-self", 129 | "justify-content", 130 | "order", 131 | "margin", 132 | "margin-top", 133 | "margin-right", 134 | "margin-bottom", 135 | "margin-left", 136 | "padding", 137 | "padding-top", 138 | "padding-right", 139 | "padding-bottom", 140 | "padding-left", 141 | "min-width", 142 | "min-height", 143 | "max-width", 144 | "max-height", 145 | "width", 146 | "height", 147 | "float", 148 | "clear", 149 | "clip", 150 | "visibility", 151 | "overflow", 152 | "border", 153 | "border-top", 154 | "border-right", 155 | "border-bottom", 156 | "border-left", 157 | "border-width", 158 | "border-top-width", 159 | "border-right-width", 160 | "border-bottom-width", 161 | "border-left-width", 162 | "border-style", 163 | "border-top-style", 164 | "border-right-style", 165 | "border-bottom-style", 166 | "border-left-style", 167 | "border-radius", 168 | "border-top-left-radius", 169 | "border-top-right-radius", 170 | "border-bottom-right-radius", 171 | "border-bottom-left-radius", 172 | "border-color", 173 | "border-top-color", 174 | "border-right-color", 175 | "border-bottom-color", 176 | "border-left-color", 177 | "box-shadow" 178 | ] 179 | }, 180 | { 181 | "emptyLineBefore": "never", 182 | "properties": [ 183 | "background", 184 | "background-attachment", 185 | "background-clip", 186 | "background-color", 187 | "background-image", 188 | "background-repeat", 189 | "background-position", 190 | "background-size" 191 | ] 192 | }, 193 | { 194 | "emptyLineBefore": "never", 195 | "properties": [ 196 | "color", 197 | "font", 198 | "font-family", 199 | "font-size", 200 | "font-smoothing", 201 | "font-style", 202 | "font-variant", 203 | "font-weight", 204 | "letter-spacing", 205 | "line-height", 206 | "list-style", 207 | "text-align", 208 | "text-decoration", 209 | "text-indent", 210 | "text-overflow", 211 | "text-rendering", 212 | "text-shadow", 213 | "text-transform", 214 | "text-wrap", 215 | "vertical-align", 216 | "white-space", 217 | "word-spacing" 218 | ] 219 | } 220 | ], 221 | { 222 | "severity": "warning" 223 | } 224 | ] 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint-config-airbnb", 5 | "tslint-eslint-rules", 6 | "tslint-react" 7 | ], 8 | "jsRules": {}, 9 | "rulesDirectory": [], 10 | "rules": { 11 | "arrow-parens": false, 12 | "whitespace": [ 13 | false, 14 | "check-branch", 15 | "check-decl", 16 | "check-operator", 17 | "check-separator", 18 | "check-type" 19 | ], 20 | "arrow-return-shorthand": [false], 21 | "semicolon": [true, "never"], 22 | "block-spacing": [ 23 | true, 24 | "never" 25 | ], 26 | "comment-format": [true, "check-space"], 27 | "function-name": [false], 28 | "import-blacklist": [true, "rxjs"], 29 | "interface-over-type-literal": false, 30 | "interface-name": false, 31 | "naming-convention": [true, 32 | {"type": "default", "format": "camelCase", "leadingUnderscore": "allow", "trailingUnderscore": "forbid"}, 33 | {"type": "variable", "format": ["camelCase","UPPER_CASE","PascalCase"]}, 34 | {"type": "variable", "modifiers": ["global", "const"], "format": ["camelCase","UPPER_CASE","PascalCase"]}, 35 | {"type": "variable", "modifiers": ["export", "const"], "format": ["camelCase","UPPER_CASE"]}, 36 | {"type": "functionVariable", "modifiers": ["export", "const"], "leadingUnderscore": "forbid", "format": "camelCase"}, 37 | {"type": "parameter", "modifiers": "unused", "leadingUnderscore": "allow"}, 38 | {"type": "member", "modifiers": "private", "leadingUnderscore": "require"}, 39 | {"type": "member", "modifiers": "protected", "leadingUnderscore": "require"}, 40 | {"type": "method", "filter": "^toJSON$", "format": null}, 41 | {"type": "property", "modifiers": ["public", "static", "const"], "format": "UPPER_CASE"}, 42 | {"type": "type", "format": "PascalCase"}, 43 | {"type": "typeAlias", "format": "PascalCase", "prefix": "T"}, 44 | {"type": "class", "modifiers": "abstract"}, 45 | {"type": "interface", "format": "PascalCase", "prefix": "I"}, 46 | {"type": "genericTypeParameter", "format": "PascalCase", "prefix": "T"}, 47 | {"type": "enumMember", "format": "PascalCase"} 48 | ], 49 | "max-line-length": [true, 120], 50 | "member-access": false, 51 | "member-ordering": [true, { "order": "fields-first" }], 52 | "newline-before-return": false, 53 | "no-any": false, 54 | "no-empty-interface": false, 55 | "no-import-side-effect": [true], 56 | "no-inferrable-types": [true, "ignore-params", "ignore-properties"], 57 | "no-invalid-this": [true, "check-function-in-method"], 58 | "no-null-keyword": false, 59 | "no-require-imports": false, 60 | "no-submodule-imports": [false], 61 | "no-this-assignment": [true, { "allow-destructuring": true }], 62 | "no-trailing-whitespace": true, 63 | "no-var-requires": false, 64 | "object-literal-sort-keys": false, 65 | "object-literal-shorthand": false, 66 | "one-variable-per-declaration": [false], 67 | "only-arrow-functions": [true, "allow-declarations"], 68 | "ordered-imports": [false], 69 | "prefer-method-signature": false, 70 | "prefer-template": [true, "allow-single-concat"], 71 | "no-unused-variable": true, 72 | "no-restricted-globals": [2, "find"], 73 | "no-cond-assign": [1, "always"], 74 | "trailing-comma": [true, { 75 | "singleline": "never", 76 | "multiline": { 77 | "objects": "never", 78 | "arrays": "never", 79 | "functions": "never", 80 | "typeLiterals": "ignore" 81 | } 82 | }], 83 | "newline-per-chained-call": [false], 84 | "triple-equals": [true, "allow-null-check"], 85 | "type-literal-delimiter": false, 86 | "typedef": [true,"parameter", "property-declaration"], 87 | "variable-name": [true, "ban-keywords", "check-format", "allow-pascal-case", "allow-leading-underscore"], 88 | "jsx-no-lambda": false, 89 | "ter-arrow-parens": [false], 90 | "ter-indent": [ 91 | false, 92 | 2, 93 | { 94 | "SwitchCase": 4 95 | } 96 | ], 97 | "quotemark": [true, "single", "jsx-single"], 98 | "import-name": [false], 99 | "no-restricted-imports": [ 100 | 1, 101 | "lodash" 102 | ], 103 | "new-parens": true, 104 | "no-plusplus": [1, { "allowForLoopAfterthoughts": true }], 105 | "prefer-promise-reject-errors": [1, { "allowEmptyReject": false }], 106 | "one-var": [1, "never"], 107 | "max-len": [2, { "code": 120, "ignoreStrings": true }], 108 | "dot-location": [2, "property"], 109 | "operator-linebreak": [ 110 | 2, 111 | "after", 112 | { 113 | "overrides": { 114 | ">": "before", 115 | ">=": "before", 116 | "<": "before", 117 | "<=": "before", 118 | "||": "before", 119 | "&&": "before", 120 | "+": "before", 121 | "-": "before" 122 | } 123 | } 124 | ], 125 | "max-statements": [2, 15], 126 | "max-depth": [1, 2], 127 | "complexity": [2, 10], 128 | "max-params": [1, 3], 129 | "max-nested-callbacks": [2, 3], 130 | "prefer-const": true, 131 | "no-param-reassign": [ 132 | 1, 133 | { 134 | "props": false 135 | } 136 | ], 137 | "no-console": false, 138 | "comma-dangle": [2, "never"], 139 | "func-style": [ 140 | 2, 141 | "declaration", 142 | { 143 | "allowArrowFunctions": true 144 | } 145 | ], 146 | "newline-after-var": [2, "always"], 147 | "new-cap": [ 148 | 2, 149 | { 150 | "capIsNewExceptions": ["Nothing", "T", "F"], 151 | "newIsCap": false 152 | } 153 | ], 154 | "no-unused-expressions": [ 155 | 2, 156 | { 157 | "allowShortCircuit": true, 158 | "allowTernary": true 159 | } 160 | ], 161 | "no-underscore-dangle": [ 162 | 2, 163 | { 164 | "allow": ["_exception", "__html"] 165 | } 166 | ], 167 | "jsx-quotes": [2, "prefer-single"], 168 | "react/jsx-indent": [4, "spaces"], 169 | "react/prefer-stateless-function": [ 170 | 1, 171 | { 172 | "ignorePureComponents": true 173 | } 174 | ], 175 | "react/require-optimization": [ 176 | 1, 177 | { "allowDecorators": ["pureRender", "connect"] } 178 | ], 179 | "react/forbid-prop-types": [ 180 | 2, 181 | { 182 | "forbid": ["any"] 183 | } 184 | ], 185 | "quote-props": [1, "consistent-as-needed"], 186 | "react/display-name": [ 187 | 1, 188 | { 189 | "ignoreTranspilerName": false 190 | } 191 | ], 192 | "react/jsx-indent-props": [1, 2], 193 | "react/no-multi-comp": [ 194 | 1, 195 | { 196 | "ignoreStateless": true 197 | } 198 | ], 199 | "react/jsx-handler-names": [ 200 | 1, 201 | { 202 | "eventHandlerPrefix": "handle", 203 | "eventHandlerPropPrefix": "on" 204 | } 205 | ], 206 | "react/jsx-max-props-per-line": [1, { "maximum": 2 }], 207 | "react/sort-comp": [ 208 | 2, 209 | { 210 | "order": [ 211 | "static-methods", 212 | "mixins", 213 | "displayName", 214 | "actions", 215 | "contextTypes", 216 | "childContextTypes", 217 | "propTypes", 218 | "defaultProps", 219 | "pure", 220 | "statics", 221 | "state", 222 | "constructor", 223 | "getDefaultProps", 224 | "getInitialState", 225 | "getChildContext", 226 | "getStoresState", 227 | "componentWillMount", 228 | "componentDidMount", 229 | "componentWillReceiveProps", 230 | "shouldComponentUpdate", 231 | "componentWillUpdate", 232 | "componentDidUpdate", 233 | "componentWillUnmount", 234 | "/^component.+$/", 235 | "/^get.+$/", 236 | "/^on.+$/", 237 | "/^handle.+$/", 238 | "everything-else", 239 | "/^render.+$/", 240 | "render" 241 | ] 242 | } 243 | ], 244 | "import/no-unresolved": [ 245 | 2, 246 | { 247 | "commonjs": true, 248 | "amd": false 249 | } 250 | ], 251 | "import/extensions": [ 252 | 1, 253 | "always", 254 | { 255 | "js": "never", 256 | "jsx": "always" 257 | } 258 | ], 259 | "no-multiple-empty-lines": [ 260 | 2, 261 | { 262 | "max": 1 263 | } 264 | ] 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "plugin:import/react"], 3 | "parser": "babel-eslint", 4 | "plugins": [ 5 | "react", 6 | "react-hooks", 7 | "import", 8 | "jest" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "node": true, 13 | "jest/globals": true 14 | }, 15 | "settings": { 16 | "import/parser": "babel-eslint", 17 | "import/ignore": [ 18 | "node_modules" 19 | ], 20 | "import/resolver": { 21 | "webpack": { 22 | "config": "./config/webpackConfigFactory.js" 23 | }, 24 | "node": { 25 | "paths": [ 26 | "./" 27 | ], 28 | "extensions": [".js",".jsx",".ts",".tsx"] 29 | } 30 | }, 31 | "react": { 32 | "pragma": "React", 33 | "version": "16.3" 34 | } 35 | }, 36 | "globals": { 37 | "jest": true, 38 | "react_disableWarnings": true, 39 | "react_enableWarnings": true, 40 | "__NODE_ENV__": false, 41 | "__PORT__": false, 42 | "__DEV__": false, 43 | "__TEST__": false, 44 | "__PROD__": false, 45 | "__COVERAGE__": false 46 | }, 47 | "rules": { 48 | "react-hooks/rules-of-hooks": "error", 49 | "indent": [ 50 | 1, 51 | 2, 52 | { 53 | "SwitchCase": 1 54 | } 55 | ], 56 | "import/extensions": [1, "always", { 57 | "js": "never", 58 | "jsx": "never", 59 | "json": "never", 60 | "ts": "never", 61 | "tsx": "never", 62 | ".tsx": "never" 63 | }], 64 | "import/no-named-as-default": 0, 65 | "linebreak-style": [1, "unix"], 66 | "no-restricted-imports": [1, "lodash"], 67 | "no-restricted-globals": [2, "find"], 68 | "no-var": 0, 69 | "no-shadow": 1, 70 | "vars-on-top": 0, 71 | "consistent-return": 1, 72 | "no-unused-vars": ["error", { "vars": "all", "args": "all", "ignoreRestSiblings": false, "argsIgnorePattern": "^nextProps|^nextState" }], 73 | "no-cond-assign": [1, "always"], 74 | "default-case": 1, 75 | "no-use-before-define": 1, 76 | "one-var-declaration-per-line": 1, 77 | "no-confusing-arrow": 1, 78 | "arrow-body-style": 0, 79 | "prefer-arrow-callback": 1, 80 | "no-case-declarations": 1, 81 | "newline-per-chained-call": 1, 82 | "no-restricted-syntax": 1, 83 | "guard-for-in": 1, 84 | "no-mixed-operators": 0, 85 | "no-continue": 1, 86 | "func-name-matching": 1, 87 | "prefer-template": 1, 88 | "no-useless-escape": 1, 89 | "new-parens": 1, 90 | "class-methods-use-this": 1, 91 | "no-return-assign": 1, 92 | "no-plusplus": [1, { "allowForLoopAfterthoughts": true }], 93 | "no-restricted-properties": 1, 94 | "prefer-promise-reject-errors": [1, {"allowEmptyReject": false}], 95 | "one-var": [ 96 | 1, 97 | "never" 98 | ], 99 | "object-curly-newline": ["error", { "multiline": true, "consistent": true }], 100 | "max-len": [2, { "code": 120, "ignoreStrings": true }], 101 | "quotes": ["error", "single", { "avoidEscape": true }], 102 | "dot-location": [ 103 | 2, 104 | "property" 105 | ], 106 | "operator-linebreak": [ 107 | 2, 108 | "after", 109 | { 110 | "overrides": { 111 | ">": "before", 112 | ">=": "before", 113 | "<": "before", 114 | "<=": "before", 115 | "||": "before", 116 | "&&": "before", 117 | "+": "before", 118 | "-": "before" 119 | } 120 | } 121 | ], 122 | "max-statements": [ 123 | 2, 124 | 15 125 | ], 126 | "max-depth": [ 127 | 1, 128 | 2 129 | ], 130 | "complexity": [ 131 | 2, 132 | 10 133 | ], 134 | "max-params": [ 135 | 1, 136 | 3 137 | ], 138 | "max-nested-callbacks": [ 139 | 2, 140 | 3 141 | ], 142 | "space-before-function-paren": [ 143 | 2, 144 | "never" 145 | ], 146 | "semi": ["error", "always"], 147 | "prefer-const": 1, 148 | "no-param-reassign": [ 149 | 1, 150 | { 151 | "props": false 152 | } 153 | ], 154 | "dot-notation": "off", 155 | "allowAfterThis": true, 156 | "no-console": "off", 157 | "curly": 0, 158 | "comma-dangle": [ 159 | 2, 160 | "never" 161 | ], 162 | "func-style": [ 163 | 2, 164 | "declaration", 165 | { 166 | "allowArrowFunctions": true 167 | } 168 | ], 169 | "newline-after-var": [ 170 | 2, 171 | "always" 172 | ], 173 | "new-cap": [ 174 | 2, 175 | { 176 | "capIsNewExceptions": [ 177 | "When", 178 | "Then", 179 | "Given", 180 | "Nothing", 181 | "T", 182 | "F" 183 | ], 184 | "newIsCap": false, 185 | "capIsNew": false 186 | } 187 | ], 188 | "no-unused-expressions": [ 189 | 2, 190 | { 191 | "allowShortCircuit": true, 192 | "allowTernary": true 193 | } 194 | ], 195 | "no-underscore-dangle": [ 196 | 0, 197 | { 198 | "allowAfterThis": true, 199 | "allow": [ 200 | "__REDUX_DEVTOOLS_EXTENSION__", 201 | "__REDUX_DEVTOOLS_EXTENSION_COMPOSE__", 202 | "__DEV__", 203 | "__INITIAL_STATE__", 204 | "_exception", 205 | "__html" 206 | ] 207 | } 208 | ], 209 | "arrow-parens": [ 210 | 2, 211 | "as-needed", 212 | { 213 | "requireForBlockBody": false 214 | } 215 | ], 216 | "jsx-quotes": [ 217 | 2, 218 | "prefer-single" 219 | ], 220 | "react/jsx-indent": [ 221 | 1, 222 | 2 223 | ], 224 | "jsx-a11y/img-has-alt": 0, 225 | "jsx-a11y/aria-role": 1, 226 | "jsx-a11y/label-has-for": 0, 227 | "jsx-a11y/href-no-hash": 0, 228 | "jsx-a11y/html-has-lang": 1, 229 | "jsx-a11y/no-static-element-interactions": 1, 230 | "jsx-a11y/anchor-has-content": 1, 231 | "jsx-a11y/no-noninteractive-element-interactions": 1, 232 | "jsx-a11y/alt-text": 1, 233 | "jsx-a11y/iframe-has-title": 1, 234 | "jsx-a11y/no-autofocus": 1, 235 | "jsx-a11y/media-has-caption": 1, 236 | "jsx-a11y/no-noninteractive-tabindex": 1, 237 | "jsx-a11y/no-noninteractive-element-to-interactive-role": 1, 238 | "jsx-a11y/interactive-supports-focus": 1, 239 | "react/no-array-index-key": 1, 240 | "react/no-will-update-set-state": 1, 241 | "react/require-default-props": 1, 242 | "react/style-prop-object": 0, 243 | "react/jsx-first-prop-new-line": 1, 244 | "react/jsx-one-expression-per-line": 0, 245 | "react/prefer-stateless-function": [ 246 | 1, 247 | { 248 | "ignorePureComponents": true 249 | } 250 | ], 251 | "react/jsx-no-bind": 2, 252 | "react/no-direct-mutation-state": 2, 253 | "react/jsx-key": 2, 254 | "react/no-find-dom-node": 1, 255 | "react/require-optimization": [1, {"allowDecorators": ["pureRender", "connect"]}], 256 | "react/jsx-filename-extension": 0, 257 | "react/jsx-no-target-blank": 2, 258 | "react/no-children-prop": 1, 259 | "react/forbid-prop-types": [ 260 | 2, 261 | { 262 | "forbid": ["any"] 263 | } 264 | ], 265 | "quote-props": [ 266 | 1, 267 | "as-needed" 268 | ], 269 | "react/prop-types": 2, 270 | "react/display-name": [ 271 | 1, 272 | { 273 | "ignoreTranspilerName": false 274 | } 275 | ], 276 | "react/jsx-indent-props": [ 277 | 1, 278 | 2 279 | ], 280 | "react/no-multi-comp": [ 281 | 1, 282 | { 283 | "ignoreStateless": true 284 | } 285 | ], 286 | "react/jsx-handler-names": [ 287 | 1, 288 | { 289 | "eventHandlerPrefix": "_handle", 290 | "eventHandlerPropPrefix": "on" 291 | } 292 | ], 293 | "no-else-return": [ 294 | "error", 295 | { 296 | "allowElseIf": true 297 | } 298 | ], 299 | "react/jsx-max-props-per-line": [1, { "maximum": 2 }], 300 | "react/jsx-wrap-multilines": 0, 301 | "react/no-unused-prop-types": 1, 302 | "react/sort-comp": [ 303 | 2, 304 | { 305 | "order": [ 306 | "static-methods", 307 | "mixins", 308 | "displayName", 309 | "actions", 310 | "contextTypes", 311 | "childContextTypes", 312 | "propTypes", 313 | "defaultProps", 314 | "pure", 315 | "statics", 316 | "state", 317 | "constructor", 318 | "getDefaultProps", 319 | "getInitialState", 320 | "getChildContext", 321 | "getStoresState", 322 | "componentWillMount", 323 | "componentDidMount", 324 | "componentWillReceiveProps", 325 | "shouldComponentUpdate", 326 | "componentWillUpdate", 327 | "componentDidUpdate", 328 | "componentWillUnmount", 329 | "/^component.+$/", 330 | "/^get.+$/", 331 | "/^on.+$/", 332 | "/^handle.+$/", 333 | "everything-else", 334 | "/^render.+$/", 335 | "render" 336 | ] 337 | } 338 | ], 339 | "import/no-unresolved": [ 340 | 2, 341 | { 342 | "commonjs": true, 343 | "amd": false 344 | } 345 | ], 346 | "import/no-commonjs": [0, { "allowPrimitiveModules": true }], 347 | "import/named": 2, 348 | "import/namespace": 2, 349 | "import/default": 2, 350 | "import/prefer-default-export": 1, 351 | "import/newline-after-import": 0, 352 | "import/unambiguous": 0, 353 | "import/no-webpack-loader-syntax": 1, 354 | "import/first": 1, 355 | "import/no-dynamic-require": 1, 356 | "import/no-deprecated": 1, 357 | "import/no-extraneous-dependencies": 0, 358 | "jest/no-disabled-tests": "warn", 359 | "jest/no-focused-tests": "error", 360 | "jest/no-identical-title": "error", 361 | "jest/valid-expect": "error", 362 | "no-multiple-empty-lines": [2, { 363 | "max": 1 364 | }] 365 | } 366 | } 367 | --------------------------------------------------------------------------------