diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c163077
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.vscode/
+build/
+node_modules/
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..b2095be
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,4 @@
+{
+ "semi": false,
+ "singleQuote": true
+}
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..133caad
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,8 @@
+module.exports = api => {
+ // caching the babel config
+ api.cache.using(() => process.env.NODE_ENV)
+ return {
+ presets: ['@babel/preset-env', '@babel/preset-react']
+ // plugins: [api.env('development') && 'react-refresh/babel'].filter(Boolean)
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..eb52c8c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "ebermergen",
+ "version": "0.1.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "start": "webpack-dev-server --mode development",
+ "build": "webpack --mode production",
+ "build-fresh": "rm -rf build/ && npm run build",
+ "serve": "ws -d build --compress --hostname 0.0.0.0",
+ "test": "tests",
+ "lint": "eslint src",
+ "format": "prettier --write src/**/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@localhost:ebermergen"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..bff025d
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+ plugins: [
+ require('postcss-flexbugs-fixes'),
+ require('postcss-preset-env'),
+ require('postcss-normalize')
+ ]
+}
diff --git a/src/App.css b/src/App.css
new file mode 100644
index 0000000..3377be8
--- /dev/null
+++ b/src/App.css
@@ -0,0 +1,30 @@
+body,
+html {
+ margin: 0;
+ padding: 0;
+ font-family: Arial, Helvetica, sans-serif;
+ line-height: 1.5;
+ background-color: #fce6cb;
+}
+
+html {
+ overflow-y: scroll;
+}
+
+header {
+ border-bottom: 2px solid white;
+ margin-bottom: 1em;
+ display: flex;
+ align-items: center;
+ padding: 0px 10px;
+}
+
+header h1 {
+ flex: 1;
+}
+
+footer {
+ border-top: 2px solid white;
+ margin-top: 1em;
+ padding: 0 10px;
+}
diff --git a/src/App.js b/src/App.js
new file mode 100644
index 0000000..e24581a
--- /dev/null
+++ b/src/App.js
@@ -0,0 +1,17 @@
+import React from 'react'
+
+import './App.css'
+
+export const App = () => {
+ return (
+ <>
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..a6fc942
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,15 @@
+import 'regenerator-runtime/runtime'
+
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import { App } from './App'
+
+function createRootElement() {
+ const body = document.getElementsByTagName('body')[0]
+ const root = document.createElement('div')
+ root.setAttribute('id', 'root')
+ body.appendChild(root)
+ return root
+}
+ReactDOM.render(, createRootElement())
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..dba1be0
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,93 @@
+const path = require('path')
+
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
+const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
+ .BundleAnalyzerPlugin
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
+const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
+const WebpackBar = require('webpackbar')
+
+module.exports = (env, argv) => {
+ const isEnvProduction = argv.mode === 'production'
+ const isEnvDevelopment = argv.mode === 'development'
+
+ return {
+ output: {
+ path: path.resolve(__dirname, 'build'),
+ filename: 'static/js/[name].[contenthash:8].js',
+ chunkFilename: 'static/js/[name].[contenthash:8].chunk.js'
+ },
+ devtool: isEnvProduction ? 'source-map' : 'cheap-module-source-map',
+ module: {
+ rules: [
+ // process javascript with babel
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ loader: 'babel-loader'
+ },
+ // process css. css modules are enabled.
+ {
+ test: /\.css$/,
+ use: [
+ isEnvDevelopment && 'style-loader',
+ isEnvProduction && MiniCssExtractPlugin.loader,
+ {
+ loader: 'css-loader',
+ options: {
+ modules: {
+ localIdentName: isEnvProduction
+ ? '[hash:base64]'
+ : '[path][name]__[local]'
+ }
+ }
+ },
+ 'postcss-loader'
+ ].filter(Boolean)
+ }
+ ]
+ },
+ optimization: {
+ minimize: isEnvProduction,
+ splitChunks: {
+ chunks: 'all',
+ name: false
+ },
+ runtimeChunk: {
+ name: entrypoint => `runtime-${entrypoint.name}`
+ }
+ },
+ plugins: [
+ // creat an index.html
+ new HtmlWebpackPlugin(),
+ // show a progress bar
+ new WebpackBar(),
+ // create a report.html for bundle size
+ // extract css to css files
+ isEnvProduction &&
+ new MiniCssExtractPlugin({
+ filename: 'static/css/[name].[contenthash:8].css',
+ chunkFilename: 'static/css/[name].[contenthash:8].chunk.css'
+ }),
+ // use cssnano to minify css
+ isEnvProduction &&
+ new OptimizeCssAssetsPlugin({
+ cssProcessorOptions: {
+ map: { inline: false, annotation: true }
+ }
+ }),
+ isEnvProduction &&
+ new BundleAnalyzerPlugin({
+ analyzerMode: 'static',
+ openAnalyzer: false
+ })
+ // hot reload not working right now
+ // isEnvDevelopment &&
+ // new ReactRefreshWebpackPlugin({ disableRefreshCheck: true })
+ ].filter(Boolean),
+ devServer: {
+ stats: 'minimal'
+ }
+ }
+}