├── client ├── apiClient.js ├── index.js ├── webpack.config.js └── components │ └── App.jsx ├── server ├── index.js ├── public │ └── index.html └── server.js ├── README.md ├── .gitignore └── package.json /client/apiClient.js: -------------------------------------------------------------------------------- 1 | import request from 'superagent' 2 | 3 | export function getGreeting() { 4 | return request.get('/greeting').then((res) => res.body.greeting) 5 | } 6 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const server = require('./server') 2 | 3 | const port = process.env.PORT || 3000 4 | 5 | server.listen(port, function () { 6 | // eslint-disable-next-line no-console 7 | console.log('Listening on port', port) 8 | }) 9 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import App from './components/App' 5 | 6 | document.addEventListener('DOMContentLoaded', () => { 7 | ReactDOM.render(, document.getElementById('app')) 8 | }) 9 | -------------------------------------------------------------------------------- /server/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React FTW! 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const express = require('express') 3 | const cors = require('cors') 4 | 5 | const server = express() 6 | 7 | server.use(express.json()) 8 | server.use(express.static(path.join(__dirname, './public'))) 9 | server.use(cors('*')) 10 | 11 | server.get('/greeting', (req, res) => { 12 | const greetings = ['hola', 'hi', 'hello', 'howdy'] 13 | let index = Math.floor(Math.random() * greetings.length) 14 | console.log(index) 15 | res.json({ greeting: greetings[index] }) 16 | }) 17 | 18 | module.exports = server 19 | -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | entry: path.join(__dirname, 'index.js'), 5 | output: { 6 | path: path.join(__dirname, '../server/public'), 7 | filename: 'bundle.js', 8 | }, 9 | mode: 'development', 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.jsx?$/, 14 | loader: 'babel-loader', 15 | exclude: /node_modules/, 16 | }, 17 | ], 18 | }, 19 | resolve: { 20 | extensions: ['.js', '.jsx'], 21 | }, 22 | devtool: 'source-map', 23 | devServer: { 24 | contentBase: path.join(__dirname, '../server/public'), 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /client/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { getGreeting } from '../apiClient' 3 | 4 | const App = () => { 5 | const [greeting, setGreeting] = useState('') 6 | const [count, setCount] = useState(0) 7 | const [isError, setIsError] = useState(false) 8 | 9 | useEffect(() => { 10 | getGreeting() 11 | .then((greeting) => { 12 | console.log(greeting) 13 | setGreeting(greeting) 14 | setIsError(false) 15 | return null 16 | }) 17 | .catch((err) => { 18 | console.log(err) 19 | setIsError(true) 20 | }) 21 | }, [count]) 22 | 23 | return ( 24 | <> 25 | {count} 26 |

{greeting}

27 | {isError && ( 28 |

29 | There was an error retrieving the greeting. 30 |

31 | )} 32 | 33 | 34 | ) 35 | } 36 | 37 | export default App 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A starter webpack project for React 2 | 3 | This is a starter project that uses webpack to transpile and bundle ES6 React code. To use, consider these steps: 4 | 5 | * Fork this repo 6 | * Rename your repo according to the app you're building 7 | 8 | ```sh 9 | git clone https://github.com/[your-account]/[your-app].git 10 | cd [your-app] && npm i 11 | ``` 12 | 13 | To start the development server with a watcher that rebuilds your code, run `npm run dev`. The assets built by webpack are placed in `server/public`. This folder is defined as a static folder in an Express.js server that can be started with `npm run server`. 14 | 15 | Additional components should be placed in `client/components`. 16 | 17 | ## Separate client/server 18 | 19 | The boilerplate is also set up to host the client using `webpack-dev-server` with hot module reloading etc. To use this method, in one terminal run: 20 | ```sh 21 | npm run client 22 | ``` 23 | and in the other: 24 | ```sh 25 | npm run server 26 | ``` 27 | The client will be available on http://localhost:8080 and the server on http://localhost:3000. Note that you will still need to manage CORS between the two, as they are on different ports. 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Webpack bundles 64 | bundle.* 65 | 66 | # Development databases 67 | dev.sqlite3 68 | 69 | tags 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "boilerplate-react-webpack", 3 | "version": "0.2.3", 4 | "description": "A webpack React starter", 5 | "repository": "https://github.com/dev-academy-challenges/boilerplate-react-webpack.git", 6 | "main": "server/index.js", 7 | "author": "EDA", 8 | "license": "ISC", 9 | "scripts": { 10 | "client": "webpack serve --config ./client/webpack.config.js --hot", 11 | "dev": "run-p dev:client dev:server", 12 | "dev:client": "npm run webpack -- --watch", 13 | "dev:server": "nodemon server", 14 | "start": "node server", 15 | "build": "npm run webpack", 16 | "server": "node server", 17 | "test": "jest", 18 | "test:watch": "jest --watchAll", 19 | "webpack": "webpack --config ./client/webpack.config.js", 20 | "lint": "eslint --ext .js,.jsx ." 21 | }, 22 | "keywords": [ 23 | "frontend" 24 | ], 25 | "babel": { 26 | "presets": [ 27 | "@babel/preset-env", 28 | "@babel/preset-react" 29 | ], 30 | "plugins": [ 31 | "@babel/plugin-proposal-class-properties", 32 | "@babel/plugin-proposal-object-rest-spread", 33 | "@babel/plugin-transform-runtime" 34 | ] 35 | }, 36 | "eslintConfig": { 37 | "extends": "eda/react", 38 | "ignorePatterns": [ 39 | "bundle.js" 40 | ] 41 | }, 42 | "dependencies": { 43 | "cors": "^2.8.5", 44 | "express": "^4.17.1" 45 | }, 46 | "devDependencies": { 47 | "@babel/core": "7.13.1", 48 | "@babel/plugin-proposal-class-properties": "7.13.0", 49 | "@babel/plugin-proposal-object-rest-spread": "7.13.0", 50 | "@babel/plugin-transform-runtime": "7.13.1", 51 | "@babel/preset-env": "7.13.0", 52 | "@babel/preset-react": "7.12.13", 53 | "@testing-library/react": "11.2.5", 54 | "babel-loader": "^8.2.2", 55 | "eslint": "^8.9.0", 56 | "eslint-config-eda": "^1.1.0", 57 | "eslint-plugin-import": "^2.25.4", 58 | "eslint-plugin-jest": "^26.1.0", 59 | "eslint-plugin-node": "^11.1.0", 60 | "eslint-plugin-prettier": "^4.0.0", 61 | "eslint-plugin-promise": "^6.0.0", 62 | "eslint-plugin-react": "^7.28.0", 63 | "jest": "^26.6.3", 64 | "nodemon": "^2.0.7", 65 | "npm-run-all": "^4.1.5", 66 | "prettier": "^2.5.1", 67 | "react": "^17.0.1", 68 | "react-dom": "^17.0.1", 69 | "superagent": "6.1.0", 70 | "webpack": "5.24.0", 71 | "webpack-cli": "4.5.0", 72 | "webpack-dev-server": "^3.11.2" 73 | }, 74 | "prettier": { 75 | "semi": false, 76 | "singleQuote": true 77 | } 78 | } 79 | --------------------------------------------------------------------------------