diff --git a/config/env/dev.env.js b/config/env/dev.env.js
new file mode 100644
index 00000000..53cd5489
--- /dev/null
+++ b/config/env/dev.env.js
@@ -0,0 +1,3 @@
+module.exports = {
+ NODE_ENV: "development"
+};
diff --git a/config/env/prod.env.js b/config/env/prod.env.js
new file mode 100644
index 00000000..2f2efdd6
--- /dev/null
+++ b/config/env/prod.env.js
@@ -0,0 +1,3 @@
+module.exports = {
+ NODE_ENV: "production"
+};
diff --git a/config/env/staging.env.js b/config/env/staging.env.js
new file mode 100644
index 00000000..ff531e2f
--- /dev/null
+++ b/config/env/staging.env.js
@@ -0,0 +1,3 @@
+module.exports = {
+ NODE_ENV: "staging"
+};
diff --git a/config/helpers.js b/config/helpers.js
new file mode 100644
index 00000000..f0e84664
--- /dev/null
+++ b/config/helpers.js
@@ -0,0 +1,15 @@
+"use strict";
+
+const path = require("path");
+
+const _root = path.resolve(__dirname, "..");
+
+exports.root = function(args) {
+ args = Array.prototype.slice.call(arguments, 0);
+
+ return path.join.apply(path, [_root].concat(args));
+};
+
+exports.assetsPath = function(_path) {
+ return path.posix.join("static", _path);
+};
diff --git a/config/webpack.config.common.js b/config/webpack.config.common.js
new file mode 100644
index 00000000..df7f6397
--- /dev/null
+++ b/config/webpack.config.common.js
@@ -0,0 +1,61 @@
+"use strict";
+
+const VueLoaderPlugin = require("vue-loader/lib/plugin");
+const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
+const helpers = require("./helpers");
+const isDev = process.env.NODE_ENV === "development";
+
+const webpackConfig = function(isDev) {
+ return {
+ entry: {
+ main: ["@babel/polyfill", helpers.root("frontend", "main")]
+ },
+ resolve: {
+ extensions: [".js", ".vue"],
+ alias: {
+ vue$: isDev ? "vue/dist/vue.runtime.js" : "vue/dist/vue.runtime.min.js",
+ "@": helpers.root("frontend")
+ }
+ },
+ module: {
+ rules: [
+ {
+ test: /\.vue$/,
+ loader: "vue-loader",
+ include: [helpers.root("frontend")]
+ },
+ {
+ test: /\.js$/,
+ loader: "babel-loader",
+ include: [helpers.root("frontend")]
+ },
+ {
+ test: /\.css$/,
+ use: [
+ isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
+ { loader: "css-loader", options: { sourceMap: isDev } }
+ ]
+ },
+ {
+ test: /\.scss$/,
+ use: [
+ isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
+ { loader: "css-loader", options: { sourceMap: isDev } },
+ { loader: "sass-loader", options: { sourceMap: isDev } }
+ ]
+ },
+ {
+ test: /\.sass$/,
+ use: [
+ isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
+ { loader: "css-loader", options: { sourceMap: isDev } },
+ { loader: "sass-loader", options: { sourceMap: isDev } }
+ ]
+ }
+ ]
+ },
+ plugins: [new VueLoaderPlugin()]
+ };
+};
+
+module.exports = webpackConfig;
diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js
new file mode 100644
index 00000000..db6de142
--- /dev/null
+++ b/config/webpack.config.dev.js
@@ -0,0 +1,45 @@
+"use strict";
+
+const webpack = require("webpack");
+const merge = require("webpack-merge");
+const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
+const HtmlPlugin = require("html-webpack-plugin");
+const helpers = require("./helpers");
+const commonConfig = require("./webpack.config.common");
+const environment = require("./env/dev.env");
+
+const webpackConfig = merge(commonConfig(true), {
+ mode: "development",
+ devtool: "cheap-module-eval-source-map",
+ output: {
+ path: helpers.root("dist"),
+ publicPath: "/",
+ filename: "js/[name].bundle.js",
+ chunkFilename: "js/[id].chunk.js"
+ },
+ optimization: {
+ runtimeChunk: "single",
+ splitChunks: {
+ chunks: "all"
+ }
+ },
+ plugins: [
+ new webpack.EnvironmentPlugin(environment),
+ new webpack.HotModuleReplacementPlugin(),
+ new FriendlyErrorsPlugin(),
+ new HtmlPlugin({ template: "frontend/index.html", chunksSortMode: "dependency" })
+ ],
+ devServer: {
+ compress: true,
+ historyApiFallback: true,
+ hot: true,
+ open: true,
+ overlay: true,
+ port: 8000,
+ stats: {
+ normal: true
+ }
+ }
+});
+
+module.exports = webpackConfig;
\ No newline at end of file
diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js
new file mode 100644
index 00000000..17c22d09
--- /dev/null
+++ b/config/webpack.config.prod.js
@@ -0,0 +1,55 @@
+"use strict";
+
+const webpack = require("webpack");
+const merge = require("webpack-merge");
+const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
+const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
+const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
+const CompressionPlugin = require("compression-webpack-plugin");
+const helpers = require("./helpers");
+const commonConfig = require("./webpack.config.common");
+const isProd = process.env.NODE_ENV === "production";
+const environment = isProd
+ ? require("./env/prod.env")
+ : require("./env/staging.env");
+
+const webpackConfig = merge(commonConfig(false), {
+ mode: "production",
+ output: {
+ path: helpers.root("dist"),
+ publicPath: "/",
+ filename: "js/[name].bundle.js"
+ },
+ optimization: {
+ minimizer: [
+ new OptimizeCSSAssetsPlugin({
+ cssProcessorPluginOptions: {
+ preset: ["default", { discardComments: { removeAll: true } }]
+ }
+ }),
+ new UglifyJSPlugin({
+ cache: true,
+ parallel: false,
+ sourceMap: !isProd
+ })
+ ]
+ },
+ plugins: [
+ new webpack.EnvironmentPlugin(environment),
+ new MiniCSSExtractPlugin({
+ filename: "css/[name].css"
+ })
+ ]
+});
+
+if (!isProd) {
+ webpackConfig.devtool = "source-map";
+
+ if (process.env.npm_config_report) {
+ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
+ .BundleAnalyzerPlugin;
+ webpackConfig.plugins.push(new BundleAnalyzerPlugin());
+ }
+}
+
+module.exports = webpackConfig;
diff --git a/frontend/App.vue b/frontend/App.vue
new file mode 100644
index 00000000..7eda4b4c
--- /dev/null
+++ b/frontend/App.vue
@@ -0,0 +1,15 @@
+
+ hello world
+