diff --git a/package.json b/package.json
new file mode 100644
index 0000000..bf9a054
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "http-server-practice",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "start": "node ./src/server.js",
+ "build": "webpack --config webpack.config.js"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@babel/preset-react": "^7.0.0",
+ "css-loader": "^3.2.0",
+ "extract-text-webpack-plugin": "^3.0.2",
+ "nodemon": "^1.19.1",
+ "react": "^16.9.0",
+ "react-dom": "^16.9.0",
+ "style-loader": "^1.0.0",
+ "websocket": "^1.0.29"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.6.0",
+ "@babel/preset-env": "^7.6.0",
+ "babel-core": "^6.26.3",
+ "babel-loader": "^8.0.6",
+ "babel-preset-env": "^1.7.0",
+ "html-webpack-plugin": "^3.2.0",
+ "mini-css-extract-plugin": "^0.8.0",
+ "script-loader": "^0.7.2",
+ "webpack": "^4.40.1",
+ "webpack-cli": "^3.3.7"
+ }
+}
diff --git a/public/client.js b/public/client.js
new file mode 100644
index 0000000..d8e1080
--- /dev/null
+++ b/public/client.js
@@ -0,0 +1,13 @@
+var W3CWebSocket = require('websocket').w3cwebsocket;
+
+var client = new W3CWebSocket('ws://localhost:3000/', 'echo-protocol');
+
+client.onerror = e => {
+ console.log('Connection Error', e);
+};
+
+client.onclose = () => {
+ console.log('echo-protocol Client Closed');
+};
+
+module.exports = client;
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..4aca288
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/src/App.js b/public/src/App.js
new file mode 100644
index 0000000..629fc0b
--- /dev/null
+++ b/public/src/App.js
@@ -0,0 +1,28 @@
+import React, { useState, useLayoutEffect } from 'react';
+import Car from './components/Car'
+import Canvas from './components/Canvas';
+
+
+export default props => {
+ const [otherPlayers,setOtherPlayers] = useState([...props.otherPlayerData]);
+
+
+ document.addEventListener('add-player', (event) => {
+ setOtherPlayers([...otherPlayers,])
+ })
+
+ useLayoutEffect(() => {
+ },[otherPlayers])
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/public/src/components/Canvas/css/canvas.css b/public/src/components/Canvas/css/canvas.css
new file mode 100644
index 0000000..4b67825
--- /dev/null
+++ b/public/src/components/Canvas/css/canvas.css
@@ -0,0 +1,10 @@
+.canvas-main {
+ height: 100vh;
+ width: 100vw;
+ background-color: grey;
+}
+
+* {
+ padding: 0px;
+ margin: 0px;
+}
\ No newline at end of file
diff --git a/public/src/components/Canvas/index.js b/public/src/components/Canvas/index.js
new file mode 100644
index 0000000..b827c6f
--- /dev/null
+++ b/public/src/components/Canvas/index.js
@@ -0,0 +1,63 @@
+import React, {useState, useLayoutEffect} from 'react';
+const client = require('../../../client.js');
+
+
+
+import './css/canvas.css';
+
+const Canvas = props => {
+ const { angle:a, xPos:x, yPos:y, id } = props;
+ const [angle, setAngle] = useState(a);
+ const [xPos, setXPos] = useState(x);
+ const [yPos,setYPos] = useState(y);
+
+ useLayoutEffect(() => {
+ },[])
+
+ useLayoutEffect(() => {
+ console.log('xPos',xPos, 'yPos',yPos,'angle', angle,"cosine sine values", Math.cos(angle* Math.PI / 180.0),Math.sin(angle* Math.PI / 180.0));
+ const car = document.getElementById(id).style;
+ car.left = `${xPos}px`;
+ car.top = `${yPos}px`;
+ car.transform = `rotate(${angle}deg)`;
+ client.send(JSON.stringify({action:"UPDATE_PLAYER",data:{id,xPos,yPos,angle}}));
+ },[xPos,yPos,angle]);
+
+
+ const handleKeyPress = event => {
+
+ const { keyCode } = event;
+
+ const xDirection = Math.cos(angle* Math.PI / 180.0);
+ const yDirection = Math.sin(angle* Math.PI / 180.0)
+
+ switch(keyCode) {
+ case 37:
+ setAngle(angle-5);
+ break;
+ case 38:
+ setXPos(xPos+5*xDirection);
+ setYPos(yPos+5*yDirection);
+
+ break;
+ case 39:
+ setAngle(angle+5);
+ break;
+ case 40:
+ setXPos(xPos-5*xDirection);
+ setYPos(yPos-5*yDirection);
+ break;
+ default:
+
+ }
+
+
+ }
+ return (
+
+ {props.children}
+
+ );
+}
+
+export default Canvas;
\ No newline at end of file
diff --git a/public/src/components/Car/css/car.css b/public/src/components/Car/css/car.css
new file mode 100644
index 0000000..aee8927
--- /dev/null
+++ b/public/src/components/Car/css/car.css
@@ -0,0 +1,8 @@
+.car-main {
+ position: relative;
+ background: linear-gradient(0.25turn, #3f87a6, #ebf8e1, #f69d3c);
+ height: 12px;
+ width: 30px;
+ transition: .15s;
+ transform:rotate(0deg);
+}
\ No newline at end of file
diff --git a/public/src/components/Car/index.js b/public/src/components/Car/index.js
new file mode 100644
index 0000000..6ab7220
--- /dev/null
+++ b/public/src/components/Car/index.js
@@ -0,0 +1,18 @@
+import React from 'react';
+
+import './css/car.css';
+const Car = props => {
+ const { id, yPos, xPos,angle } = props;
+
+
+
+
+ return (
+
+
+
+ );
+}
+
+
+export default Car;
\ No newline at end of file
diff --git a/public/src/index.js b/public/src/index.js
new file mode 100644
index 0000000..d6e602c
--- /dev/null
+++ b/public/src/index.js
@@ -0,0 +1,71 @@
+import React from 'react';
+const client = require('../client.js');
+import ReactDOM from 'react-dom';
+import Car from './components/Car';
+import App from './App';
+
+
+client.onopen = () => {
+ console.log("Browser connected to the server!!");
+
+}
+
+function isEquivalent(a, b) {
+ // Create arrays of property names
+ var aProps = Object.getOwnPropertyNames(a);
+ var bProps = Object.getOwnPropertyNames(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (var i = 0; i < aProps.length; i++) {
+ var propName = aProps[i];
+
+ // If values of same property are not equal,
+ // objects are not equivalent
+ if (a[propName] !== b[propName]) {
+ return false;
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+}
+
+
+client.onmessage = (e) => {
+ // console.log(e)
+ const {action, data, otherPlayerData} = JSON.parse(e.data);
+ switch(action){
+ case "INIATE":
+ console.log("iniate")
+ const otherCars = otherPlayerData.map(other => !isEquivalent(data,other) && )
+ ReactDOM.render(,document.getElementById('root'));
+ break;
+ case "NEW_PLAYER":
+ console.log("new-player")
+ document.dispatchEvent(new CustomEvent('add-player',{
+ detail: {
+ data
+ }
+ }));
+ break;
+ case "UPDATE_CANVAS":
+ console.log('update')
+ const modifiedCar = document.getElementById(data.id);
+ modifiedCar.style.top = data.yPos;
+ modifiedCar.style.left = data.xPos;
+ modifiedCar.style.transform = `rotate(${data.angle}deg)`
+ break;
+ default:
+ console.log(data);
+ }
+
+};
+
+
+
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..fda568c
--- /dev/null
+++ b/server.js
@@ -0,0 +1,204 @@
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+const WebSocketServer = require('websocket').server;
+
+const indexPath = path.join(__dirname,'/public/index.html');
+const jsPath = path.join(__dirname,'../dist/bundle.js');
+const cssPath = path.join(__dirname,'../dist/style.css');
+
+
+
+
+let carData = [];
+let clients = []
+
+
+
+
+
+const server = http.createServer((req,res) => {
+ if(req.url==="/"){
+ fs.readFile(indexPath, (err, file) => {
+ if(err) {
+ res.send(500,{error: err});
+ }
+ res.writeHeader(200, {"Content-Type": "text/html"});
+ res.write(file);
+ res.end()
+
+ })
+ }
+
+ if(req.url==="/bundle.js") {
+ fs.readFile(jsPath, (err, file) => {
+ if(err) {
+ console.log("Error getting the javaScript")
+ }
+ res.writeHeader(200, {"Content-Type": "text/javascript"});
+ res.write(file);
+ res.end();
+ })
+ }
+
+
+ if(req.url==="/styles.css") {
+ fs.readFile(cssPath, (err, file ) => {
+ if(err) {
+ console.log("Error getting the css")
+ }
+ res.writeHeader(200, {"Content-Type": "text/css"});
+ res.write(file);
+ res.end();
+ })
+
+
+ }
+});
+server.listen(3000,'127.0.0.1');
+
+
+const broadcastExcept = (action,data,connection) => {
+ clients.map( socket => {
+
+ if(!isEquivalent(connection,socket))
+ socket.send(JSON.stringify(
+ {
+ action,
+ data
+ }));
+ })
+}
+
+const broadcastAll = (action,data) => {
+ clients.map( socket => {
+ socket.send(JSON.stringify(
+ {
+ action,
+ data
+ }));
+ })
+}
+
+const updateCars = (newData) => {
+ return carData.map(car => {
+ if(car.id === newData.id){
+ return newData;
+ }
+
+ return car;
+ })
+}
+
+
+const webSocketServer = new WebSocketServer({httpServer:server});
+
+webSocketServer.on('request', request => {
+ const data = {
+ id: uuid(),
+ xPos: randomXPos(),
+ yPos: randomXPos(),
+ angle: randomAngle()
+ }
+
+ broadcastAll("NEW_PLAYER",data);
+
+ var connection = request.accept('echo-protocol', request.origin);
+
+ console.log((new Date()) + ' Connection accepted.');
+
+
+
+ carData.push(data);
+ console.log('8888888888888888888888888888888',carData);
+ connection.send(JSON.stringify(
+ {
+ action: 'INIATE',
+ data,
+ otherPlayerData:carData
+ }))
+
+
+ // setInterval(() => {
+ // connection.send(JSON.stringify({
+ // action:"test",
+ // data:data.id
+ // }))
+ // }, 1000);
+
+ clients.push(connection);
+ connection.on('message', message => {
+ const _message = JSON.parse(message.utf8Data);
+ switch(_message.action){
+ case "UPDATE_PLAYER":
+ carData = updateCars(_message.data)
+ console.log("New Canvas Positions", carData);
+ broadcastExcept("UPDATE_CANVAS",_message.data,connection);
+ break;
+ default:
+ // console.log(_message);
+ }
+
+ });
+
+
+ connection.on('close', (reasonCode, description) => {
+ console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
+ });
+});
+
+
+
+
+
+const uuid = () => {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+}
+
+const randomXPos = () => {
+ return Math.floor((Math.random() * 200) + 25);
+}
+
+const randomYPos = () => {
+ return Math.floor((Math.random() * 200) + 25);
+}
+
+
+
+const randomAngle = () => {
+ return Math.floor((Math.random() * 360));
+}
+
+const randomColor = () => {
+
+}
+
+
+function isEquivalent(a, b) {
+ // Create arrays of property names
+ var aProps = Object.getOwnPropertyNames(a);
+ var bProps = Object.getOwnPropertyNames(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (var i = 0; i < aProps.length; i++) {
+ var propName = aProps[i];
+
+ // If values of same property are not equal,
+ // objects are not equivalent
+ if (a[propName] !== b[propName]) {
+ return false;
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+}
\ No newline at end of file
diff --git a/src/public/client.js b/src/public/client.js
new file mode 100644
index 0000000..d8e1080
--- /dev/null
+++ b/src/public/client.js
@@ -0,0 +1,13 @@
+var W3CWebSocket = require('websocket').w3cwebsocket;
+
+var client = new W3CWebSocket('ws://localhost:3000/', 'echo-protocol');
+
+client.onerror = e => {
+ console.log('Connection Error', e);
+};
+
+client.onclose = () => {
+ console.log('echo-protocol Client Closed');
+};
+
+module.exports = client;
\ No newline at end of file
diff --git a/src/public/index.html b/src/public/index.html
new file mode 100644
index 0000000..4aca288
--- /dev/null
+++ b/src/public/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/public/src/App.js b/src/public/src/App.js
new file mode 100644
index 0000000..629fc0b
--- /dev/null
+++ b/src/public/src/App.js
@@ -0,0 +1,28 @@
+import React, { useState, useLayoutEffect } from 'react';
+import Car from './components/Car'
+import Canvas from './components/Canvas';
+
+
+export default props => {
+ const [otherPlayers,setOtherPlayers] = useState([...props.otherPlayerData]);
+
+
+ document.addEventListener('add-player', (event) => {
+ setOtherPlayers([...otherPlayers,])
+ })
+
+ useLayoutEffect(() => {
+ },[otherPlayers])
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/public/src/components/Canvas/css/canvas.css b/src/public/src/components/Canvas/css/canvas.css
new file mode 100644
index 0000000..4b67825
--- /dev/null
+++ b/src/public/src/components/Canvas/css/canvas.css
@@ -0,0 +1,10 @@
+.canvas-main {
+ height: 100vh;
+ width: 100vw;
+ background-color: grey;
+}
+
+* {
+ padding: 0px;
+ margin: 0px;
+}
\ No newline at end of file
diff --git a/src/public/src/components/Canvas/index.js b/src/public/src/components/Canvas/index.js
new file mode 100644
index 0000000..b827c6f
--- /dev/null
+++ b/src/public/src/components/Canvas/index.js
@@ -0,0 +1,63 @@
+import React, {useState, useLayoutEffect} from 'react';
+const client = require('../../../client.js');
+
+
+
+import './css/canvas.css';
+
+const Canvas = props => {
+ const { angle:a, xPos:x, yPos:y, id } = props;
+ const [angle, setAngle] = useState(a);
+ const [xPos, setXPos] = useState(x);
+ const [yPos,setYPos] = useState(y);
+
+ useLayoutEffect(() => {
+ },[])
+
+ useLayoutEffect(() => {
+ console.log('xPos',xPos, 'yPos',yPos,'angle', angle,"cosine sine values", Math.cos(angle* Math.PI / 180.0),Math.sin(angle* Math.PI / 180.0));
+ const car = document.getElementById(id).style;
+ car.left = `${xPos}px`;
+ car.top = `${yPos}px`;
+ car.transform = `rotate(${angle}deg)`;
+ client.send(JSON.stringify({action:"UPDATE_PLAYER",data:{id,xPos,yPos,angle}}));
+ },[xPos,yPos,angle]);
+
+
+ const handleKeyPress = event => {
+
+ const { keyCode } = event;
+
+ const xDirection = Math.cos(angle* Math.PI / 180.0);
+ const yDirection = Math.sin(angle* Math.PI / 180.0)
+
+ switch(keyCode) {
+ case 37:
+ setAngle(angle-5);
+ break;
+ case 38:
+ setXPos(xPos+5*xDirection);
+ setYPos(yPos+5*yDirection);
+
+ break;
+ case 39:
+ setAngle(angle+5);
+ break;
+ case 40:
+ setXPos(xPos-5*xDirection);
+ setYPos(yPos-5*yDirection);
+ break;
+ default:
+
+ }
+
+
+ }
+ return (
+
+ {props.children}
+
+ );
+}
+
+export default Canvas;
\ No newline at end of file
diff --git a/src/public/src/components/Car/css/car.css b/src/public/src/components/Car/css/car.css
new file mode 100644
index 0000000..aee8927
--- /dev/null
+++ b/src/public/src/components/Car/css/car.css
@@ -0,0 +1,8 @@
+.car-main {
+ position: relative;
+ background: linear-gradient(0.25turn, #3f87a6, #ebf8e1, #f69d3c);
+ height: 12px;
+ width: 30px;
+ transition: .15s;
+ transform:rotate(0deg);
+}
\ No newline at end of file
diff --git a/src/public/src/components/Car/index.js b/src/public/src/components/Car/index.js
new file mode 100644
index 0000000..6ab7220
--- /dev/null
+++ b/src/public/src/components/Car/index.js
@@ -0,0 +1,18 @@
+import React from 'react';
+
+import './css/car.css';
+const Car = props => {
+ const { id, yPos, xPos,angle } = props;
+
+
+
+
+ return (
+
+
+
+ );
+}
+
+
+export default Car;
\ No newline at end of file
diff --git a/src/public/src/index.js b/src/public/src/index.js
new file mode 100644
index 0000000..d6e602c
--- /dev/null
+++ b/src/public/src/index.js
@@ -0,0 +1,71 @@
+import React from 'react';
+const client = require('../client.js');
+import ReactDOM from 'react-dom';
+import Car from './components/Car';
+import App from './App';
+
+
+client.onopen = () => {
+ console.log("Browser connected to the server!!");
+
+}
+
+function isEquivalent(a, b) {
+ // Create arrays of property names
+ var aProps = Object.getOwnPropertyNames(a);
+ var bProps = Object.getOwnPropertyNames(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (var i = 0; i < aProps.length; i++) {
+ var propName = aProps[i];
+
+ // If values of same property are not equal,
+ // objects are not equivalent
+ if (a[propName] !== b[propName]) {
+ return false;
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+}
+
+
+client.onmessage = (e) => {
+ // console.log(e)
+ const {action, data, otherPlayerData} = JSON.parse(e.data);
+ switch(action){
+ case "INIATE":
+ console.log("iniate")
+ const otherCars = otherPlayerData.map(other => !isEquivalent(data,other) && )
+ ReactDOM.render(,document.getElementById('root'));
+ break;
+ case "NEW_PLAYER":
+ console.log("new-player")
+ document.dispatchEvent(new CustomEvent('add-player',{
+ detail: {
+ data
+ }
+ }));
+ break;
+ case "UPDATE_CANVAS":
+ console.log('update')
+ const modifiedCar = document.getElementById(data.id);
+ modifiedCar.style.top = data.yPos;
+ modifiedCar.style.left = data.xPos;
+ modifiedCar.style.transform = `rotate(${data.angle}deg)`
+ break;
+ default:
+ console.log(data);
+ }
+
+};
+
+
+
\ No newline at end of file
diff --git a/src/server.js b/src/server.js
new file mode 100644
index 0000000..fda568c
--- /dev/null
+++ b/src/server.js
@@ -0,0 +1,204 @@
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+const WebSocketServer = require('websocket').server;
+
+const indexPath = path.join(__dirname,'/public/index.html');
+const jsPath = path.join(__dirname,'../dist/bundle.js');
+const cssPath = path.join(__dirname,'../dist/style.css');
+
+
+
+
+let carData = [];
+let clients = []
+
+
+
+
+
+const server = http.createServer((req,res) => {
+ if(req.url==="/"){
+ fs.readFile(indexPath, (err, file) => {
+ if(err) {
+ res.send(500,{error: err});
+ }
+ res.writeHeader(200, {"Content-Type": "text/html"});
+ res.write(file);
+ res.end()
+
+ })
+ }
+
+ if(req.url==="/bundle.js") {
+ fs.readFile(jsPath, (err, file) => {
+ if(err) {
+ console.log("Error getting the javaScript")
+ }
+ res.writeHeader(200, {"Content-Type": "text/javascript"});
+ res.write(file);
+ res.end();
+ })
+ }
+
+
+ if(req.url==="/styles.css") {
+ fs.readFile(cssPath, (err, file ) => {
+ if(err) {
+ console.log("Error getting the css")
+ }
+ res.writeHeader(200, {"Content-Type": "text/css"});
+ res.write(file);
+ res.end();
+ })
+
+
+ }
+});
+server.listen(3000,'127.0.0.1');
+
+
+const broadcastExcept = (action,data,connection) => {
+ clients.map( socket => {
+
+ if(!isEquivalent(connection,socket))
+ socket.send(JSON.stringify(
+ {
+ action,
+ data
+ }));
+ })
+}
+
+const broadcastAll = (action,data) => {
+ clients.map( socket => {
+ socket.send(JSON.stringify(
+ {
+ action,
+ data
+ }));
+ })
+}
+
+const updateCars = (newData) => {
+ return carData.map(car => {
+ if(car.id === newData.id){
+ return newData;
+ }
+
+ return car;
+ })
+}
+
+
+const webSocketServer = new WebSocketServer({httpServer:server});
+
+webSocketServer.on('request', request => {
+ const data = {
+ id: uuid(),
+ xPos: randomXPos(),
+ yPos: randomXPos(),
+ angle: randomAngle()
+ }
+
+ broadcastAll("NEW_PLAYER",data);
+
+ var connection = request.accept('echo-protocol', request.origin);
+
+ console.log((new Date()) + ' Connection accepted.');
+
+
+
+ carData.push(data);
+ console.log('8888888888888888888888888888888',carData);
+ connection.send(JSON.stringify(
+ {
+ action: 'INIATE',
+ data,
+ otherPlayerData:carData
+ }))
+
+
+ // setInterval(() => {
+ // connection.send(JSON.stringify({
+ // action:"test",
+ // data:data.id
+ // }))
+ // }, 1000);
+
+ clients.push(connection);
+ connection.on('message', message => {
+ const _message = JSON.parse(message.utf8Data);
+ switch(_message.action){
+ case "UPDATE_PLAYER":
+ carData = updateCars(_message.data)
+ console.log("New Canvas Positions", carData);
+ broadcastExcept("UPDATE_CANVAS",_message.data,connection);
+ break;
+ default:
+ // console.log(_message);
+ }
+
+ });
+
+
+ connection.on('close', (reasonCode, description) => {
+ console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
+ });
+});
+
+
+
+
+
+const uuid = () => {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+}
+
+const randomXPos = () => {
+ return Math.floor((Math.random() * 200) + 25);
+}
+
+const randomYPos = () => {
+ return Math.floor((Math.random() * 200) + 25);
+}
+
+
+
+const randomAngle = () => {
+ return Math.floor((Math.random() * 360));
+}
+
+const randomColor = () => {
+
+}
+
+
+function isEquivalent(a, b) {
+ // Create arrays of property names
+ var aProps = Object.getOwnPropertyNames(a);
+ var bProps = Object.getOwnPropertyNames(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (var i = 0; i < aProps.length; i++) {
+ var propName = aProps[i];
+
+ // If values of same property are not equal,
+ // objects are not equivalent
+ if (a[propName] !== b[propName]) {
+ return false;
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..cc9329e
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,31 @@
+const path = require('path');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+
+
+module.exports = {
+ mode: "development",
+ entry: ["./src/public/src/index.js","./src/public/client.js"],
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "bundle.js",
+ libraryTarget: "umd"
+ },
+ module: {
+ rules: [
+ {
+ test: /\.(js|jsx)$/,
+ exclude: /node_modules/,
+ use: ['babel-loader']
+ },
+ {
+ test: /\.css$/,
+ use: [ 'style-loader', MiniCssExtractPlugin.loader, 'css-loader']
+ }
+ ]
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ filename: 'style.css'
+ })
+ ]
+}
\ No newline at end of file