├── .flowconfig ├── .gitignore ├── .foreverignore ├── src ├── screens │ ├── main.css │ ├── results │ │ ├── index.js │ │ └── Container.js │ ├── app │ │ ├── loading │ │ │ ├── index.js │ │ │ ├── View.js │ │ │ ├── main.css │ │ │ ├── Container.js │ │ │ └── __tests__ │ │ │ │ └── Container.test.js │ │ ├── actions.js │ │ ├── index.js │ │ ├── reducer.js │ │ ├── Container.js │ │ └── __tests__ │ │ │ └── Container.test.js │ ├── index.js │ ├── reducers.js │ └── home │ │ └── index.js ├── components │ └── property-card │ │ ├── index.js │ │ ├── main.css │ │ └── View.js ├── shared │ ├── polyfills.js │ ├── fetchData.js │ └── routes.js ├── server │ ├── ignoreExtensions.js │ ├── pages │ │ ├── index.js │ │ ├── error.js │ │ └── default.js │ ├── mockAPI.js │ ├── index.js │ ├── mockData.json │ └── middleware.js ├── app.js ├── types │ └── Property.js └── client │ └── index.js ├── __mocks__ ├── fileMock.js └── styleMock.js ├── .babelrc ├── config └── webpack.config.js ├── README.md ├── package.json └── .eslintrc.js /.flowconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | public -------------------------------------------------------------------------------- /.foreverignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | node_modules 3 | public 4 | src/client/* -------------------------------------------------------------------------------- /src/screens/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #f0f0f0; 3 | } 4 | -------------------------------------------------------------------------------- /src/screens/results/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Container'; -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | // __mocks__/fileMock.js 2 | 3 | module.exports = {}; -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | // __mocks__/styleMock.js 2 | 3 | module.exports = {}; -------------------------------------------------------------------------------- /src/components/property-card/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default } from './View'; -------------------------------------------------------------------------------- /src/screens/app/loading/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export { default } from './Container'; -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-2" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/shared/polyfills.js: -------------------------------------------------------------------------------- 1 | require('es6-promise').polyfill(); 2 | require('isomorphic-fetch'); -------------------------------------------------------------------------------- /src/server/ignoreExtensions.js: -------------------------------------------------------------------------------- 1 | const noop = () => null; 2 | 3 | require.extensions['.css'] = noop; 4 | -------------------------------------------------------------------------------- /src/server/pages/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './default'; 2 | export { default as error } from './error'; -------------------------------------------------------------------------------- /src/screens/app/actions.js: -------------------------------------------------------------------------------- 1 | export const DATA_LOADED = 'DATA_LOADED'; 2 | export const DATA_FETCHING = 'DATA_FETCHING'; -------------------------------------------------------------------------------- /src/screens/app/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Container'; 2 | export { default as reducer } from './reducer'; -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | require('./shared/polyfills'); 2 | require('./server/ignoreExtensions'); 3 | require('babel-register'); 4 | require('./server'); -------------------------------------------------------------------------------- /src/types/Property.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type Property = { 4 | image: string, 5 | address: string, 6 | type: string, 7 | } -------------------------------------------------------------------------------- /src/screens/app/loading/View.js: -------------------------------------------------------------------------------- 1 | import './main.css' 2 | import React from 'react'; 3 | 4 | export default () =>
Loading...
; 5 | -------------------------------------------------------------------------------- /src/screens/index.js: -------------------------------------------------------------------------------- 1 | import './main.css'; 2 | export { default as App } from './app'; 3 | export { default as Home } from './home'; 4 | export { default as Results } from './results'; -------------------------------------------------------------------------------- /src/server/mockAPI.js: -------------------------------------------------------------------------------- 1 | import mockData from './mockData.json' 2 | 3 | export default (_, response) => { 4 | response.setHeader('Content-Type', 'application/json'); 5 | response.send(mockData); 6 | }; -------------------------------------------------------------------------------- /src/screens/reducers.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://redux.js.org/docs/recipes/reducers/UsingCombineReducers.html 3 | */ 4 | import { combineReducers } from 'redux' 5 | import { reducer as apiData } from './app'; 6 | 7 | export default combineReducers({ apiData }); 8 | -------------------------------------------------------------------------------- /src/shared/fetchData.js: -------------------------------------------------------------------------------- 1 | const emptyPromise = () => Promise.resolve(); 2 | 3 | export default ({ routes }) => { 4 | const matchedRoute = routes[routes.length - 1]; 5 | const fetchData = matchedRoute.fetchData || emptyPromise; 6 | 7 | return fetchData(); 8 | } -------------------------------------------------------------------------------- /src/server/pages/error.js: -------------------------------------------------------------------------------- 1 | export default (app) => ` 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
${app}
10 | 11 | 12 | `; -------------------------------------------------------------------------------- /src/components/property-card/main.css: -------------------------------------------------------------------------------- 1 | .property-card { 2 | overflow: hidden; 3 | width:400px; 4 | margin-bottom: 10px; 5 | border-radius: 4px; 6 | font-family: Helvetica; 7 | background:#fff; 8 | border-bottom: solid 2px #ccc; 9 | } 10 | 11 | .property-card__content { 12 | text-align:center; 13 | padding: 5px; 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, IndexRoute } from 'react-router'; 3 | import { App, Home, Results } from '../screens'; 4 | 5 | export default ( 6 | 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/screens/app/loading/main.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | position: fixed; 3 | background: rgba(51, 63, 72, 0.95); 4 | width:75px; 5 | height: 75px; 6 | line-height: 75px; 7 | margin: -37px 0 0 -37px; 8 | border-radius:50%; 9 | top:50%; 10 | left:50%; 11 | color: #fff; 12 | text-align: center; 13 | opacity: 1; 14 | z-index: 999; 15 | visibility: visible; 16 | } 17 | -------------------------------------------------------------------------------- /src/screens/home/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router'; 3 | 4 | export default class Home extends Component { 5 | render () { 6 | return ( 7 |
8 |
Welcome to the homepage
9 | Go get some Results 10 |
11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/screens/app/loading/Container.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import View from './View'; 6 | 7 | export const Container = ({fetchingData}: {fetchingData: boolean}) => { 8 | return fetchingData ? : null; 9 | }; 10 | 11 | const mapStateToProps = ({apiData}) => { 12 | return { fetchingData: apiData.fetchingData } 13 | }; 14 | 15 | export default connect(mapStateToProps)(Container); 16 | -------------------------------------------------------------------------------- /src/screens/app/reducer.js: -------------------------------------------------------------------------------- 1 | import { DATA_LOADED, DATA_FETCHING } from './actions'; 2 | 3 | export default (state = {}, action) => { 4 | switch (action.type) { 5 | case DATA_FETCHING: { 6 | return { ...state, fetchingData: true }; 7 | } 8 | 9 | case DATA_LOADED: { 10 | const { data } = action; 11 | 12 | return { ...state, data, fetchingData: false }; 13 | } 14 | 15 | default: { 16 | return state; 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/server/pages/default.js: -------------------------------------------------------------------------------- 1 | export default (app, data) => ` 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
${app}
10 | 13 | 14 | 15 | 16 | `; -------------------------------------------------------------------------------- /src/components/property-card/View.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import './main.css'; 5 | import type { Property } from '../../types/Property'; 6 | 7 | export default ({image, address, type}: Property) => ( 8 |
9 | 10 |
11 | {type} 12 |

{address}

13 |
14 |
15 | ) -------------------------------------------------------------------------------- /src/screens/app/loading/__tests__/Container.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { Container } from '../Container'; 4 | import View from '../View'; 5 | 6 | describe("Loader", () => { 7 | const test = shallow( 8 | 9 | ); 10 | 11 | it("renders a loader when fetchingData", () => { 12 | expect(test.contains()).toBeTruthy(); 13 | }); 14 | 15 | it("renders null when not fetchingData", () => { 16 | test.setProps({fetchingData: false}); 17 | expect(test.type()).toBe(null); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import express from 'express'; 4 | import path from 'path'; 5 | import middleware from './middleware'; 6 | import mockAPI from './mockAPI'; 7 | 8 | const app = express(); 9 | 10 | app.set('host', process.env.HOST || "0.0.0.0"); 11 | app.set('port', process.env.PORT || 3001); 12 | 13 | app.use('/public', express.static(path.join(__dirname, '../../public'))); 14 | app.get('/api', mockAPI); 15 | app.use('/', middleware); 16 | 17 | app.listen(app.get('port'), app.get('host'), () => { 18 | console.log(`server listening on http://${app.get('host')}:${app.get('port')}`); 19 | }); 20 | -------------------------------------------------------------------------------- /src/server/mockData.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "image": "https://i2.au.reastatic.net/400x300/d735c9ef4a3fffe6c212a8008a89f97e1e465a64461489cd6cdadb855ad7ee0d/main.jpg", 4 | "address": "123 Fake street", 5 | "type": "house" 6 | }, 7 | { 8 | "image": "https://i2.au.reastatic.net/400x300/78505655419944e4c58c753435a6f6b6ec8934641bc724570a07733db1a48067/main.jpg", 9 | "address": "123 Smith street", 10 | "type": "house" 11 | }, 12 | { 13 | "image": "https://i2.au.reastatic.net/400x300/be36162503aca7ddc483b2ff184853a416b4c38673def09b420ab9eeb21f8fc8/main.jpg", 14 | "address": "12 George street", 15 | "type": "apartment" 16 | } 17 | ] -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | module.exports = { 3 | devtool: "#inline-source-map", 4 | "entry": "./src/client", 5 | module: { 6 | loaders: [ 7 | { 8 | test: /\.js$/, 9 | loader: 'babel-loader' 10 | }, 11 | { 12 | test: /\.json$/, 13 | loader: 'json-loader' 14 | }, 15 | { 16 | test:/\.css$/, 17 | loader: ExtractTextPlugin.extract('style-loader','css-loader') 18 | } 19 | ] 20 | }, 21 | plugins: [ 22 | new ExtractTextPlugin('styles.css') 23 | ], 24 | "output": { 25 | path: './public', 26 | filename: 'bundle.js' 27 | } 28 | }; -------------------------------------------------------------------------------- /src/screens/app/Container.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { connect } from 'react-redux'; 5 | import Loader from './loading'; 6 | 7 | type props = { 8 | apiData: { 9 | data: any, 10 | fetchingData: string 11 | } 12 | } 13 | 14 | export class App extends Component { 15 | shouldComponentUpdate (nextProps: props) { 16 | return !nextProps.apiData.fetchingData; 17 | } 18 | 19 | render () { 20 | return ( 21 |
22 | 23 | { React.cloneElement(this.props.children, { data: this.props.apiData.data }) } 24 |
25 | ); 26 | } 27 | } 28 | 29 | /* 30 | Connecting to the Redux store: 31 | http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components 32 | */ 33 | 34 | const mapStateToProps = ({ apiData }) => ({ apiData }); 35 | 36 | export default connect(mapStateToProps)(App); 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # app-with-server-rendering 2 | A basic application that demonstrates server side rendering. 3 | Each individual commit has purposefully been crafted to explain the new code that's added and its purpose 4 | within the application. 5 | 6 | ## Requirements 7 | - [Node js (7.1)](https://nodejs.org/en/download/) 8 | - [NPM](https://www.npmjs.com/package/npm) 9 | 10 | ## Optional Requirements 11 | - [NVM](https://github.com/creationix/nvm) Node version manager 12 | - [Yarn package manager](https://yarnpkg.com/) An alternative to NPM 13 | 14 | NVM is a version manager for node which is a good option if you don't have node installed use NVM: 15 | > https://github.com/creationix/nvm/blob/master/README.markdown 16 | 17 | For a smoother experience with package management it is ideal to use `yarn`over NPM 18 | `npm install -g yarn` 19 | 20 | ## Prior to starting the application 21 | `npm install` or `yarn` 22 | 23 | ## Run the application 24 | `npm run start` or `yarn start` 25 | 26 | ## Develop the application 27 | `npm run dev` or `yarn dev` 28 | This will restart the server and recompile the client side assets whenever a file has been changed 29 | within the project 30 | 31 | ## Linting and Testing 32 | `npm run lint` or `yarn lint` 33 | -------------------------------------------------------------------------------- /src/screens/results/Container.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import PropertyCard from '../../components/property-card'; 5 | import { Link } from 'react-router'; 6 | import type { Property } from '../../types/Property'; 7 | import type { Fetch } from 'isomorphic-fetch'; 8 | 9 | export default class Results extends Component { 10 | props: { 11 | data: Array 12 | } 13 | 14 | static fetchData (): Fetch { 15 | return new Promise((resolve) => { 16 | // Wait 1 second before making a request to simulate a poor connection. 17 | setTimeout(() => { 18 | fetch('http://localhost:3001/api') 19 | .then(response => response.json()) 20 | .then(properties => resolve(properties)) 21 | }, 1000); 22 | }); 23 | } 24 | 25 | renderProperties () { 26 | return this.props.data.map((property: Property, key) => ) 27 | } 28 | 29 | render () { 30 | return ( 31 |
32 | Back to Home page 33 | { this.renderProperties() } 34 |
35 | ); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/screens/app/__tests__/Container.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { App } from '../Container'; 4 | import Loader from '../loading'; 5 | 6 | describe("App", () => { 7 | // In the context of an App screen it has a child of a sub-route (screen) this could be any component, lets just create one 8 | const Screen = ({ data }) => ( 9 |
{data}
10 | ); 11 | // Lets simulate some API data 12 | const apiData = { data: 'Some API Data' }; 13 | // Shallow Mount the component to a test variable so we may apply assertions on it ( we shallow mount as we don't care about the 14 | // Output of the child components 15 | const test = shallow( 16 | 17 | 18 | 19 | ); 20 | 21 | it("renders the apps children with API data", () => { 22 | expect(test.contains([ 23 | , 24 | 25 | ])).toBe(true); 26 | }); 27 | 28 | it("won't re-render fetchingData is true", () => { 29 | const newProps = { 30 | apiData: { 31 | fetchingData: true, 32 | data: 'Change me' 33 | } 34 | }; 35 | 36 | // Calling setProps will assign the Component with the next set of props and trigger a re-render. 37 | test.setProps(newProps); 38 | expect(test.contains([])).toBe(true); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/client/index.js: -------------------------------------------------------------------------------- 1 | import '../shared/polyfills'; 2 | import {browserHistory, Router, match} from 'react-router'; 3 | import React from 'react'; 4 | import reactDOM from 'react-dom'; 5 | import routes from '../shared/routes'; 6 | import {Provider} from 'react-redux'; 7 | import {createStore} from 'redux'; 8 | import reducers from '../screens/reducers'; 9 | import fetchData from '../shared/fetchData'; 10 | import {DATA_LOADED, DATA_FETCHING} from '../screens/app/actions'; 11 | 12 | export default (() => { 13 | // Create a store from hydrated data that was populated by the server. 14 | const store = createStore(reducers, {apiData: {data: window.__hydrationData__}}); 15 | // Create an instance of our application 16 | const application = ( 17 | 18 | 19 | 20 | ); 21 | 22 | // Render the application into the DOM 23 | reactDOM.render(application, document.getElementById("wrapper")); 24 | 25 | // Listen to URL changes in the browser 26 | browserHistory.listen(location => { 27 | // Match the URL we listened to, to one of our routes 28 | match({location, routes}, (error, redirectLocation, routingState) => { 29 | // Dispatch an action to our store that we are fetching data, we block re-rendering within the app screen 30 | // Until the next set of data comes back because as soon as this callback finishes react router will render the 31 | // Next matched route, we stop this behaviour until the asynchronous route has finished fetching data. 32 | store.dispatch({type: DATA_FETCHING}); 33 | 34 | // After we have the active route we execute the fetchData method via the fetchData Module 35 | fetchData(routingState).then(data => { 36 | 37 | // Dispatch the data to our store so the screens can now render. 38 | store.dispatch({type: DATA_LOADED, data}); 39 | }); 40 | }); 41 | }); 42 | })(); 43 | -------------------------------------------------------------------------------- /src/server/middleware.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOMServer from 'react-dom/server'; 3 | import { RouterContext, match } from 'react-router'; 4 | import { Provider } from 'react-redux'; 5 | import { createStore } from 'redux'; 6 | import reducers from '../screens/reducers'; 7 | import routes from '../shared/routes'; 8 | import fetchData from '../shared/fetchData'; 9 | 10 | import page, { error as errorPage } from './pages'; 11 | 12 | export default (request, response) => { 13 | // Match the URL to a route 14 | match({location: request.url, routes}, (_, redirect, routingState) => { 15 | 16 | // Handle a URL that matches a redirect 17 | if (redirect) { 18 | response.redirect(redirect.pathname + redirect.search); 19 | } 20 | 21 | // Handle any route we don't have a screen for 22 | if (!routingState) { 23 | return response.status(404).send(errorPage(`

404

`)); 24 | } 25 | 26 | // After we have the active route we execute the fetchData method via the fetchData Module 27 | fetchData(routingState).then(data => { 28 | 29 | // Render the application to a string 30 | const applicationString = ReactDOMServer.renderToString( 31 | // Set the initial state of the store to the data that came back from the API 32 | 33 | 34 | 35 | ); 36 | 37 | // Place the application within the page template 38 | const pageResponse = page(applicationString, data); 39 | 40 | // Return the response to the browser. 41 | return response.status(200).send(pageResponse); 42 | 43 | }) 44 | .catch(error => { 45 | 46 | /* eslint-disable no-console */ 47 | console.log(error); 48 | 49 | // If there is a run time error that bubbles up from fetchData, catch it and return a 500 50 | return response.status(500).send(errorPage(`

500

`)); 51 | }); 52 | }); 53 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-with-server-rendering", 3 | "version": "0.0.1", 4 | "description": "A basic application that demonstrates serverside rendering", 5 | "repository": "git@github.com:azaharakis/app-with-server-rendering.git", 6 | "author": "Alexander Zaharakis ", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "webpack --config=./config/webpack.config.js", 10 | "start": "npm run build && node ./src/app.js", 11 | "watch": "webpack --watch --config=./config/webpack.config.js", 12 | "dev": "concurrently 'npm run watch' 'forever --watch ./src/app.js'", 13 | "lint": "eslint ./src", 14 | "flow": "flow; test $? -eq 0 -o $? -eq 2", 15 | "unit": "jest test", 16 | "unit-watch": "jest test --watch", 17 | "test": "npm run lint && npm run flow && npm run unit" 18 | }, 19 | "dependencies": { 20 | "babel-preset-env": "^1.1.4", 21 | "babel-preset-react": "^6.16.0", 22 | "babel-preset-stage-2": "^6.18.0", 23 | "babel-register": "^6.18.0", 24 | "es6-promise": "^4.0.5", 25 | "express": "^4.14.0", 26 | "isomorphic-fetch": "^2.2.1", 27 | "react": "^15.4.1", 28 | "react-dom": "^15.4.1", 29 | "react-redux": "^5.0.1", 30 | "react-router": "^3.0.0", 31 | "redux": "^3.6.0" 32 | }, 33 | "devDependencies": { 34 | "babel-core": "^6.21.0", 35 | "babel-eslint": "^7.1.1", 36 | "babel-jest": "^18.0.0", 37 | "babel-loader": "^6.2.10", 38 | "concurrently": "^3.1.0", 39 | "css-loader": "^0.26.1", 40 | "enzyme": "^2.7.0", 41 | "eslint": "^3.12.2", 42 | "eslint-plugin-flowtype": "^2.29.2", 43 | "eslint-plugin-react": "^6.8.0", 44 | "extract-text-webpack-plugin": "^1.0.1", 45 | "flow-bin": "^0.37.4", 46 | "forever": "^0.15.3", 47 | "jest": "^18.1.0", 48 | "json-loader": "^0.5.4", 49 | "react-addons-test-utils": "^15.4.1", 50 | "style-loader": "^0.13.1", 51 | "webpack": "^1.14.0" 52 | }, 53 | "jest": { 54 | "moduleNameMapper": { 55 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", 56 | "\\.(css)$": "/__mocks__/styleMock.js" 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "jest": true 8 | }, 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:flowtype/recommended" 12 | ], 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "experimentalObjectRestSpread": true, 16 | "jsx": true 17 | }, 18 | "sourceType": "module" 19 | }, 20 | "plugins": [ 21 | "react", 22 | "flowtype" 23 | ], 24 | "rules": { 25 | "react/jsx-uses-react": "error", 26 | "react/jsx-uses-vars": "error", 27 | "accessor-pairs": "error", 28 | "array-bracket-spacing": "error", 29 | "array-callback-return": "error", 30 | "arrow-spacing": [ 31 | "error", 32 | { 33 | "after": true, 34 | "before": true 35 | } 36 | ], 37 | "block-scoped-var": "error", 38 | "block-spacing": "error", 39 | "brace-style": "error", 40 | "callback-return": "error", 41 | "camelcase": "error", 42 | "capitalized-comments": "error", 43 | "comma-dangle": "error", 44 | "comma-spacing": [ 45 | "error", 46 | { 47 | "after": true, 48 | "before": false 49 | } 50 | ], 51 | "comma-style": "error", 52 | "complexity": "error", 53 | "computed-property-spacing": "error", 54 | "consistent-this": "error", 55 | "curly": "error", 56 | "default-case": "error", 57 | "dot-notation": [ 58 | "error", 59 | { 60 | "allowKeywords": true 61 | } 62 | ], 63 | "eol-last": "off", 64 | "eqeqeq": "error", 65 | "func-call-spacing": "error", 66 | "func-name-matching": "error", 67 | "func-names": "error", 68 | "func-style": "error", 69 | "generator-star-spacing": "error", 70 | "global-require": "error", 71 | "guard-for-in": "error", 72 | "handle-callback-err": "error", 73 | "id-blacklist": "error", 74 | "id-match": "error", 75 | "indent": [ 76 | "error", 77 | 4, 78 | { 79 | "SwitchCase":1 80 | } 81 | ], 82 | "init-declarations": "error", 83 | "jsx-quotes": "error", 84 | "key-spacing": "error", 85 | "keyword-spacing": "error", 86 | "line-comment-position": "error", 87 | "linebreak-style": [ 88 | "error", 89 | "unix" 90 | ], 91 | "lines-around-comment": "error", 92 | "lines-around-directive": "error", 93 | "max-depth": "error", 94 | "max-len": "off", 95 | "max-lines": "error", 96 | "max-nested-callbacks": "error", 97 | "max-params": "error", 98 | "max-statements": "error", 99 | "max-statements-per-line": "error", 100 | "new-cap": "error", 101 | "new-parens": "error", 102 | "newline-after-var": [ 103 | "error", 104 | "always" 105 | ], 106 | "newline-before-return": "error", 107 | "newline-per-chained-call": "error", 108 | "no-alert": "error", 109 | "no-array-constructor": "error", 110 | "no-await-in-loop": "error", 111 | "no-bitwise": "error", 112 | "no-caller": "error", 113 | "no-catch-shadow": "error", 114 | "no-confusing-arrow": "error", 115 | "no-continue": "error", 116 | "no-div-regex": "error", 117 | "no-duplicate-imports": "error", 118 | "no-else-return": "error", 119 | "no-empty-function": "error", 120 | "no-eq-null": "error", 121 | "no-eval": "error", 122 | "no-extend-native": "error", 123 | "no-extra-bind": "error", 124 | "no-extra-label": "error", 125 | "no-floating-decimal": "error", 126 | "no-implicit-coercion": "error", 127 | "no-implicit-globals": "error", 128 | "no-implied-eval": "error", 129 | "no-inline-comments": "error", 130 | "no-invalid-this": "error", 131 | "no-iterator": "error", 132 | "no-label-var": "error", 133 | "no-labels": "error", 134 | "no-lone-blocks": "error", 135 | "no-lonely-if": "error", 136 | "no-loop-func": "error", 137 | "no-magic-numbers": "off", 138 | "no-mixed-operators": "error", 139 | "no-mixed-requires": "error", 140 | "no-multi-spaces": "error", 141 | "no-multi-str": "error", 142 | "no-multiple-empty-lines": "error", 143 | "no-native-reassign": "error", 144 | "no-negated-condition": "error", 145 | "no-negated-in-lhs": "error", 146 | "no-nested-ternary": "error", 147 | "no-new": "error", 148 | "no-new-func": "error", 149 | "no-new-object": "error", 150 | "no-new-require": "error", 151 | "no-new-wrappers": "error", 152 | "no-octal-escape": "error", 153 | "no-param-reassign": "error", 154 | "no-path-concat": "error", 155 | "no-plusplus": "error", 156 | "no-process-env": "off", 157 | "no-process-exit": "error", 158 | "no-proto": "error", 159 | "no-prototype-builtins": "error", 160 | "no-restricted-globals": "error", 161 | "no-restricted-imports": "error", 162 | "no-restricted-modules": "error", 163 | "no-restricted-properties": "error", 164 | "no-restricted-syntax": "error", 165 | "no-return-assign": "error", 166 | "no-return-await": "error", 167 | "no-script-url": "error", 168 | "no-self-compare": "error", 169 | "no-sequences": "error", 170 | "no-shadow": "error", 171 | "no-shadow-restricted-names": "error", 172 | "no-spaced-func": "error", 173 | "no-sync": "error", 174 | "no-tabs": "error", 175 | "no-template-curly-in-string": "error", 176 | "no-throw-literal": "error", 177 | "no-trailing-spaces": "error", 178 | "no-undef-init": "error", 179 | "no-undefined": "error", 180 | "no-unmodified-loop-condition": "error", 181 | "no-unneeded-ternary": "error", 182 | "no-unused-expressions": "error", 183 | "no-use-before-define": "error", 184 | "no-useless-call": "error", 185 | "no-useless-computed-key": "error", 186 | "no-useless-concat": "error", 187 | "no-useless-constructor": "error", 188 | "no-useless-escape": "error", 189 | "no-useless-rename": "error", 190 | "no-useless-return": "error", 191 | "no-var": "error", 192 | "no-void": "error", 193 | "no-warning-comments": "error", 194 | "no-whitespace-before-property": "error", 195 | "no-with": "error", 196 | "object-curly-newline": "error", 197 | "object-shorthand": "error", 198 | "one-var": "off", 199 | "one-var-declaration-per-line": "error", 200 | "operator-assignment": "error", 201 | "operator-linebreak": "error", 202 | "padded-blocks": "off", 203 | "prefer-arrow-callback": "error", 204 | "prefer-const": "error", 205 | "prefer-numeric-literals": "error", 206 | "prefer-reflect": "error", 207 | "prefer-rest-params": "error", 208 | "prefer-spread": "error", 209 | "prefer-template": "error", 210 | "quotes": "off", 211 | "radix": "error", 212 | "require-await": "error", 213 | "require-jsdoc": "error", 214 | "rest-spread-spacing": "error", 215 | "semi": "off", 216 | "semi-spacing": "error", 217 | "sort-imports": "off", 218 | "sort-vars": "error", 219 | "space-before-blocks": "error", 220 | "space-before-function-paren": "error", 221 | "space-in-parens": [ 222 | "error", 223 | "never" 224 | ], 225 | "space-infix-ops": "error", 226 | "space-unary-ops": "error", 227 | "spaced-comment": "error", 228 | "strict": "error", 229 | "symbol-description": "error", 230 | "template-curly-spacing": [ 231 | "error", 232 | "never" 233 | ], 234 | "unicode-bom": [ 235 | "error", 236 | "never" 237 | ], 238 | "valid-jsdoc": "error", 239 | "vars-on-top": "error", 240 | "wrap-iife": "error", 241 | "wrap-regex": "error", 242 | "yield-star-spacing": "error", 243 | "yoda": "error" 244 | }, 245 | }; --------------------------------------------------------------------------------