From 05006c15e04d0619f2b3704b42622f6700204d0f Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Sun, 19 Aug 2018 12:46:36 +0300 Subject: [PATCH] Implement react server side rendering and ditch prerendering with puppeteer --- configs/webpack/common.js | 77 ++-- configs/webpack/dev.js | 41 ++- configs/webpack/prod.js | 135 +++++-- package-lock.json | 734 +++++++++++++++++++++++++++----------- package.json | 20 +- src/client/App.tsx | 9 + src/client/index.ts | 7 + src/index.scss | 3 +- src/index.tsx | 2 - src/routes.tsx | 1 + src/server/App.tsx | 9 + src/server/index.ts | 27 ++ 12 files changed, 763 insertions(+), 302 deletions(-) create mode 100644 src/client/App.tsx create mode 100644 src/client/index.ts create mode 100644 src/server/App.tsx create mode 100644 src/server/index.ts diff --git a/configs/webpack/common.js b/configs/webpack/common.js index 508de03..bf8fe48 100644 --- a/configs/webpack/common.js +++ b/configs/webpack/common.js @@ -2,59 +2,50 @@ const {resolve} = require("path"); const {CheckerPlugin} = require("awesome-typescript-loader"); const StyleLintPlugin = require("stylelint-webpack-plugin"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); const Dotenv = require("dotenv-webpack"); -module.exports = { - resolve: { +module.exports = function (env, argv) { + const config = {}; + + config.resolve = { extensions: [".ts", ".tsx", ".js", ".jsx"] - }, - context: resolve(__dirname, "../../src"), - module: { - rules: [ - { - test: /\.js$/, - use: ["babel-loader", "source-map-loader"], - exclude: /node_modules/ - }, - { - test: /\.tsx?$/, - use: ["babel-loader", "awesome-typescript-loader"] - }, - { - test: /\.css$/, - use: ["style-loader", {loader: "css-loader", options: {importLoaders: 1}}, "postcss-loader"] - }, - { - test: /\.scss$/, - loaders: [ - "style-loader", - {loader: "css-loader", options: {importLoaders: 1}}, - "postcss-loader", - "sass-loader" - ] - }, - { - test: /\.(jpe?g|png|gif|svg)$/i, - loaders: [ - "file-loader?hash=sha512&digest=hex&name=img/[hash].[ext]", - "image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false" - ] - } + }; + config.context = resolve(__dirname, "../../src"); + config.module = { + rules: [], + }; + config.module.rules.push({ + test: /\.js$/, + use: ["babel-loader", "source-map-loader"], + exclude: /node_modules/ + }); + config.module.rules.push({ + test: /\.tsx?$/, + use: ["babel-loader", "awesome-typescript-loader"] + }); + + config.module.rules.push({ + test: /\.(jpe?g|png|gif|svg)$/i, + loaders: [ + "file-loader?hash=sha512&digest=hex&name=assets/img/[hash].[ext]", + "image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false" ] - }, - plugins: [ + }); + + config.plugins = [ new CheckerPlugin(), new StyleLintPlugin(), - new HtmlWebpackPlugin({template: "index.html.ejs"}), new Dotenv({ path: "./.env.sample" }), new Dotenv({ path: "./.env" - }) - ], - performance: { + }), + ]; + + config.performance = { hints: false - } + }; + + return config; }; diff --git a/configs/webpack/dev.js b/configs/webpack/dev.js index e9164ed..8c70c3a 100644 --- a/configs/webpack/dev.js +++ b/configs/webpack/dev.js @@ -2,18 +2,20 @@ const merge = require("webpack-merge"); const webpack = require("webpack"); const FaviconsWebpackPlugin = require("favicons-webpack-plugin"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); const commonConfig = require("./common.js"); -module.exports = merge(commonConfig, { - mode: "development", - entry: [ +module.exports = function (env, argv) { + const base = commonConfig(env, argv); + base.mode = "development"; + base.entry = [ "react-hot-loader/patch", // Activate HMR for React "webpack-dev-server/client?http://0.0.0.0:8080", // Bundle the client for webpack-dev-server and connect to the provided endpoint "webpack/hot/only-dev-server", // Bundle the client for hot reloading, only- means to only hot reload for successful updates "./index.tsx" // The entry point of our app - ], - devServer: { + ]; + base.devServer = { hot: true, // Enable HMR on the server historyApiFallback: true, host: '0.0.0.0', @@ -41,9 +43,9 @@ module.exports = merge(commonConfig, { // watchOptions: { // ignored: /node_modules/ // } - }, - devtool: "cheap-module-eval-source-map", - plugins: [ + }; + base.devtool = "cheap-module-eval-source-map"; + base.plugins = base.plugins.concat([ new FaviconsWebpackPlugin({ logo: "./assets/img/favicon.png", icons: { @@ -60,6 +62,23 @@ module.exports = merge(commonConfig, { } }), new webpack.HotModuleReplacementPlugin(), // Enable HMR globally - new webpack.NamedModulesPlugin() // Prints more readable module names in the browser console on HMR updates - ] -}); + new webpack.NamedModulesPlugin(), // Prints more readable module names in the browser console on HMR updates + new HtmlWebpackPlugin({template: "index.html.ejs"}), + ]); + + base.module.rules.push({ + test: /\.css$/, + use: ["style-loader", {loader: "css-loader", options: {importLoaders: 1}}, "postcss-loader"] + }); + base.module.rules.push({ + test: /\.scss$/, + loaders: [ + "style-loader", + {loader: "css-loader", options: {importLoaders: 1}}, + "postcss-loader", + "sass-loader" + ] + }); + + return base; +}; diff --git a/configs/webpack/prod.js b/configs/webpack/prod.js index 89a7b67..57ab9a3 100644 --- a/configs/webpack/prod.js +++ b/configs/webpack/prod.js @@ -3,48 +3,113 @@ const path = require("path"); const merge = require("webpack-merge"); const resolve = path.resolve; -const DynamicCdnWebpackPlugin = require("dynamic-cdn-webpack-plugin"); -const PrerenderSPAPlugin = require("prerender-spa-plugin"); const FaviconsWebpackPlugin = require("favicons-webpack-plugin"); +const nodeExternals = require('webpack-node-externals'); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const WebpackCdnPlugin = require('webpack-cdn-plugin'); +const webpack = require('webpack'); /* NOTE: This is a list of all routes that are prerendered for production use. Please list all routes that contain search engine accessible content, i.e., stuff that you would like to find with a Google Search. */ -const PRERENDERED_ROUTES = ["/", "/404"]; const commonConfig = require("./common"); -module.exports = merge(commonConfig, { - mode: "production", - entry: "./index.tsx", - output: { - filename: "js/bundle.[hash].min.js", +module.exports = function (env, argv) { + const base = commonConfig(env, argv); + base.mode = "production"; + base.entry = "./index.tsx"; + base.output = { path: resolve(__dirname, "../../dist"), - publicPath: "/" - }, - devtool: "source-map", - plugins: [ - new FaviconsWebpackPlugin({ - logo: "./assets/img/favicon.png", - icons: { - android: true, - appleIcon: true, - appleStartup: true, - coast: true, - favicons: true, - firefox: true, - opengraph: true, - twitter: true, - yandex: true, - windows: true - } + publicPath: "/", + }; + base.devtool = "source-map"; + base.plugins.push( + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // both options are optional + filename: "assets/styles.css?[hash]", }), - new DynamicCdnWebpackPlugin(), - new PrerenderSPAPlugin({ - // Required - The path to the webpack-outputted app to prerender. - staticDir: resolve(__dirname, "../../dist"), - // Required - Routes to render. - routes: PRERENDERED_ROUTES - }) - ] -}); + ); + + if (env.platform === 'server') { + base.entry = "./server/index.ts"; + base.output.filename = 'js/server.js?[hash]'; + base.target = 'node'; + base.externals = [nodeExternals()]; + + base.module.rules.push({ + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + {loader: "css-loader", options: {importLoaders: 1}}, + "postcss-loader" + ], + }); + + base.module.rules.push({ + test: /\.scss$/, + loaders: [ + MiniCssExtractPlugin.loader, + {loader: "css-loader", options: {importLoaders: 1}}, + "postcss-loader", + "sass-loader" + ] + }); + } + + else if (env.platform === 'client') { + base.entry = './client/index.ts'; + base.output.filename = 'js/client.js?[hash]'; + base.target = 'web'; + + base.module.rules.push({ + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + {loader: "css-loader", options: {importLoaders: 1}}, + "postcss-loader" + ], + }); + + base.module.rules.push({ + test: /\.scss$/, + loaders: [ + MiniCssExtractPlugin.loader, + {loader: "css-loader", options: {importLoaders: 1}}, + "postcss-loader", + "sass-loader" + ] + }); + + base.plugins.push( + new HtmlWebpackPlugin({ + template: "index.html.ejs", + }), + new FaviconsWebpackPlugin({ + logo: "./assets/img/favicon.png", + icons: { + android: true, + appleIcon: true, + appleStartup: true, + coast: true, + favicons: true, + firefox: true, + opengraph: true, + twitter: true, + yandex: true, + windows: true + } + }), + new WebpackCdnPlugin({ + modules: [ + { name: 'react', var: 'React', path: `umd/react.${process.env.NODE_ENV}.min.js` }, + { name: 'react-dom', var: 'ReactDOM', path: `umd/react-dom.${process.env.NODE_ENV}.min.js` }, + ], + }), + ); + } + + return base; +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 55bcf98..9d150de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,27 +20,6 @@ "integrity": "sha512-LAQ1d4OPfSJ/BMbI2DuizmYrrkD9JMaTdi2hQTlI53lQ4kRQPyZQRS4CYQ7O66bnBBnP/oYdRxbk++X0xuFU6A==", "dev": true }, - "@prerenderer/prerenderer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@prerenderer/prerenderer/-/prerenderer-0.7.1.tgz", - "integrity": "sha512-BgtsmvSDxEHoR1PSFk3olruz12at68r6Dr4e6FrdwzSJsSvinxCNFsXYMxkOyYTLbhTK9GBS/G0NAYE1BERL4Q==", - "dev": true, - "requires": { - "express": "^4.16.2", - "http-proxy-middleware": "^0.18.0", - "portfinder": "^1.0.13" - } - }, - "@prerenderer/renderer-puppeteer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@prerenderer/renderer-puppeteer/-/renderer-puppeteer-0.1.4.tgz", - "integrity": "sha512-z5WbndAto2YCjPqNEGt2G9kqa7+6fJMLPSk4i/qpmLwq1N8eRwSYnbnb4JSyg9O3sgs5k5amaP+4Wh2O4bURDQ==", - "dev": true, - "requires": { - "promise-limit": "^2.5.0", - "puppeteer": "^1.0.0" - } - }, "@types/error-stack-parser": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", @@ -48,9 +27,9 @@ "dev": true }, "@types/lodash": { - "version": "4.14.110", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.110.tgz", - "integrity": "sha512-iXYLa6olt4tnsCA+ZXeP6eEW3tk1SulWeYyP/yooWfAtXjozqXgtX4+XUtMuOCfYjKGz3F34++qUc3Q+TJuIIw==", + "version": "4.14.116", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz", + "integrity": "sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==", "dev": true }, "@types/node": { @@ -263,6 +242,37 @@ "long": "^3.2.0" } }, + "@webpack-contrib/schema-utils": { + "version": "1.0.0-beta.0", + "resolved": "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz", + "integrity": "sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chalk": "^2.3.2", + "strip-ansi": "^4.0.0", + "text-table": "^0.2.0", + "webpack-log": "^1.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "@zeit/schemas": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-1.2.0.tgz", @@ -300,15 +310,6 @@ "acorn": "^5.0.0" } }, - "agent-base": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", - "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, "ajv": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.1.tgz", @@ -370,6 +371,15 @@ "string-width": "^2.0.0" } }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -391,6 +401,15 @@ "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", "dev": true }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1025,6 +1044,12 @@ "lodash": "^4.17.4" } }, + "babel-helper-evaluate-path": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz", + "integrity": "sha1-ComvcCwGshcCf6NxkI3UmJ0+Yz8=", + "dev": true + }, "babel-helper-explode-assignable-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", @@ -1048,6 +1073,12 @@ "babel-types": "^6.24.1" } }, + "babel-helper-flip-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", + "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", + "dev": true + }, "babel-helper-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", @@ -1081,6 +1112,24 @@ "babel-types": "^6.24.1" } }, + "babel-helper-is-nodes-equiv": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", + "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", + "dev": true + }, + "babel-helper-is-void-0": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", + "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=", + "dev": true + }, + "babel-helper-mark-eval-scopes": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", + "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", + "dev": true + }, "babel-helper-optimise-call-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", @@ -1115,6 +1164,12 @@ "babel-types": "^6.24.1" } }, + "babel-helper-remove-or-void": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", + "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", + "dev": true + }, "babel-helper-replace-supers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", @@ -1129,6 +1184,12 @@ "babel-types": "^6.24.1" } }, + "babel-helper-to-multiple-sequence-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.4.3.tgz", + "integrity": "sha1-W1GLESf0ezA4dzOGoVYaK0jmMrY=", + "dev": true + }, "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", @@ -1159,6 +1220,12 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-add-module-exports": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", + "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=", + "dev": true + }, "babel-plugin-check-es2015-constants": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", @@ -1168,6 +1235,101 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-minify-builtins": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.4.3.tgz", + "integrity": "sha1-nqPVn0rEp7uVjXEtKVVqH4b3+B4=", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.4.3" + } + }, + "babel-plugin-minify-constant-folding": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.4.3.tgz", + "integrity": "sha1-MA+d6N2ghEoXaxk2U5YOJK0z4ZE=", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.4.3" + } + }, + "babel-plugin-minify-dead-code-elimination": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz", + "integrity": "sha1-c2KCZYZPkAjQAnUG9Yq+s8HQLZg=", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.4.3", + "babel-helper-mark-eval-scopes": "^0.4.3", + "babel-helper-remove-or-void": "^0.4.3", + "lodash.some": "^4.6.0" + } + }, + "babel-plugin-minify-flip-comparisons": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", + "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "babel-plugin-minify-guarded-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.3.tgz", + "integrity": "sha1-zHCbRFP9IbHzAod0RMifiEJ845c=", + "dev": true, + "requires": { + "babel-helper-flip-expressions": "^0.4.3" + } + }, + "babel-plugin-minify-infinity": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", + "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=", + "dev": true + }, + "babel-plugin-minify-mangle-names": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.4.3.tgz", + "integrity": "sha1-FvG/90t6fJPfwkHngx3V+0sCPvc=", + "dev": true, + "requires": { + "babel-helper-mark-eval-scopes": "^0.4.3" + } + }, + "babel-plugin-minify-numeric-literals": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", + "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=", + "dev": true + }, + "babel-plugin-minify-replace": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.4.3.tgz", + "integrity": "sha1-nSifS6FdTmAR6HmfpfG6d+yBIZ0=", + "dev": true + }, + "babel-plugin-minify-simplify": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.4.3.tgz", + "integrity": "sha1-N3VthcYURktLCSfytOQXGR1Vc4o=", + "dev": true, + "requires": { + "babel-helper-flip-expressions": "^0.4.3", + "babel-helper-is-nodes-equiv": "^0.0.1", + "babel-helper-to-multiple-sequence-expressions": "^0.4.3" + } + }, + "babel-plugin-minify-type-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", + "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", @@ -1530,6 +1692,30 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-transform-inline-consecutive-adds": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", + "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=", + "dev": true + }, + "babel-plugin-transform-member-expression-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", + "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=", + "dev": true + }, + "babel-plugin-transform-merge-sibling-variables": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", + "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=", + "dev": true + }, + "babel-plugin-transform-minify-booleans": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", + "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", + "dev": true + }, "babel-plugin-transform-object-rest-spread": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", @@ -1540,6 +1726,15 @@ "babel-runtime": "^6.26.0" } }, + "babel-plugin-transform-property-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", + "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "babel-plugin-transform-react-display-name": { "version": "6.25.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", @@ -1589,6 +1784,33 @@ "regenerator-transform": "^0.10.0" } }, + "babel-plugin-transform-regexp-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", + "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=", + "dev": true + }, + "babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", + "dev": true + }, + "babel-plugin-transform-remove-debugger": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", + "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=", + "dev": true + }, + "babel-plugin-transform-remove-undefined": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.4.3.tgz", + "integrity": "sha1-1AsNp/kcCMBsxyt2dHTAHEiU3gI=", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.4.3" + } + }, "babel-plugin-transform-runtime": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", @@ -1598,6 +1820,12 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-transform-simplify-comparison-operators": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", + "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", + "dev": true + }, "babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", @@ -1608,6 +1836,12 @@ "babel-types": "^6.24.1" } }, + "babel-plugin-transform-undefined-to-void": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", + "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=", + "dev": true + }, "babel-polyfill": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", @@ -1674,6 +1908,37 @@ "babel-plugin-transform-flow-strip-types": "^6.22.0" } }, + "babel-preset-minify": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.4.3.tgz", + "integrity": "sha1-spw91pGJBThFmPCSuVUVLiah/g8=", + "dev": true, + "requires": { + "babel-plugin-minify-builtins": "^0.4.3", + "babel-plugin-minify-constant-folding": "^0.4.3", + "babel-plugin-minify-dead-code-elimination": "^0.4.3", + "babel-plugin-minify-flip-comparisons": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.3", + "babel-plugin-minify-infinity": "^0.4.3", + "babel-plugin-minify-mangle-names": "^0.4.3", + "babel-plugin-minify-numeric-literals": "^0.4.3", + "babel-plugin-minify-replace": "^0.4.3", + "babel-plugin-minify-simplify": "^0.4.3", + "babel-plugin-minify-type-constructors": "^0.4.3", + "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-merge-sibling-variables": "^6.9.4", + "babel-plugin-transform-minify-booleans": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-regexp-constructors": "^0.4.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "babel-plugin-transform-remove-undefined": "^0.4.3", + "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", + "babel-plugin-transform-undefined-to-void": "^6.9.4", + "lodash.isplainobject": "^4.0.6" + } + }, "babel-preset-react": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", @@ -2822,9 +3087,9 @@ "dev": true }, "chrome-remote-interface": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.25.6.tgz", - "integrity": "sha512-YNkw2FqKqb99p7nyxrx0cphAHzjyukd6NwnOuk1bI0KV5lrQ6A7DA8HZAWjhy5qbbetpGky2AKENnkTsFrYw3w==", + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.25.7.tgz", + "integrity": "sha512-6zI6LbR2IiGmduFZededaerEr9hHXabxT/L+fRrdq65a0CfyLMzpq0BKuZiqN0Upqcacsb6q2POj7fmobwBsEA==", "dev": true, "requires": { "commander": "2.11.x", @@ -2836,17 +3101,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } } } }, @@ -4340,30 +4594,6 @@ "stream-shift": "^1.0.0" } }, - "dynamic-cdn-webpack-plugin": { - "version": "4.0.0-rc.1", - "resolved": "https://registry.npmjs.org/dynamic-cdn-webpack-plugin/-/dynamic-cdn-webpack-plugin-4.0.0-rc.1.tgz", - "integrity": "sha512-yKfyj9TAeAoir3H6tS5mKVPrja8CKYWKFQYBa2/TEmLZLoSoVVHKdvSN0TNMPC2L5g2e0RoyOTBfNroU64YgYA==", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "html-webpack-include-assets-plugin": "1.0.3", - "read-pkg-up": "^3.0.0", - "resolve-pkg": "^1.0.0" - }, - "dependencies": { - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, "each-async": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", @@ -4580,15 +4810,6 @@ "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", "dev": true }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, "es6-symbol": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", @@ -6785,6 +7006,51 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, + "gulp-clone": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-clone/-/gulp-clone-2.0.1.tgz", + "integrity": "sha512-SLg/KsHBbinR/pCX3PF5l1YlR28hLp0X+bcpf77PtMJ6zvAQ5kRjtCPV5Wt1wHXsXWZN0eTUZ15R8ZYpi/CdCA==", + "dev": true, + "requires": { + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + } + } + }, + "gulp-data": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/gulp-data/-/gulp-data-1.3.1.tgz", + "integrity": "sha512-fvpQJvgVyhkwRcFP3Y9QUS9sWvIFsAlJDinQjhLuknmHZz52jH0gHmTujYBFjr9aTlTHlrAayY5m1d0tA1HzGQ==", + "dev": true, + "requires": { + "plugin-error": "^0.1.2", + "through2": "^2.0.0", + "util-extend": "^1.0.1" + }, + "dependencies": { + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + } + } + }, "gulp-decompress": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.2.0.tgz", @@ -6803,6 +7069,39 @@ "integrity": "sha512-nEuZB7/9i0IZ8AXORTizl2QLP9tcC9uWc/s329zElBLJw1CfOhmMXBxwVlCRKjDyrWuhVP0uBKl61KeQ32TiCg==", "dev": true }, + "gulp-run-command": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/gulp-run-command/-/gulp-run-command-0.0.9.tgz", + "integrity": "sha512-8/Gzh+zfe6CFLqaL+LxwqLPj5fk/N1eCHEr36K5p9Al40iuxpbVnq1aZRGXMj+4DlWBzjjAAxas3EkMNI/6Vcw==", + "dev": true, + "requires": { + "babel-plugin-transform-runtime": "6.15.0", + "cross-spawn": "4.0.0", + "spawn-args": "0.2.0", + "timeout-as-promise": "^1.0.0" + }, + "dependencies": { + "babel-plugin-transform-runtime": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.15.0.tgz", + "integrity": "sha1-PXW02Umtga8VdXAnOEb7Wa6w1Xw=", + "dev": true, + "requires": { + "babel-runtime": "^6.9.0" + } + }, + "cross-spawn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.0.tgz", + "integrity": "sha1-glR3SrR4a4xbPPTfumbOVjkywlI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } + } + }, "gulp-sourcemaps": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", @@ -7356,17 +7655,6 @@ "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "dev": true }, - "html-webpack-include-assets-plugin": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/html-webpack-include-assets-plugin/-/html-webpack-include-assets-plugin-1.0.3.tgz", - "integrity": "sha512-P8qqFWyTSBHySuWBZINZw/Xqas7bB5d8BspeJN7GgB2/1CLV/xF3GX2dqy4qlKDySOgXPYBp5z64f8Xvr5prSg==", - "dev": true, - "requires": { - "glob": "^7.1.2", - "minimatch": "^3.0.4", - "slash": "^1.0.0" - } - }, "html-webpack-plugin": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", @@ -7524,16 +7812,6 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", - "dev": true, - "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" - } - }, "husky": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/husky/-/husky-1.0.0-rc.9.tgz", @@ -8041,9 +8319,9 @@ "dev": true }, "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", "dev": true }, "is-absolute": { @@ -9215,6 +9493,12 @@ "lodash._objecttypes": "~2.4.1" } }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, "lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -9244,6 +9528,12 @@ "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "dev": true + }, "lodash.tail": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", @@ -9766,6 +10056,17 @@ "dom-walk": "^0.1.0" } }, + "mini-css-extract-plugin": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.1.tgz", + "integrity": "sha512-XWuB3G61Rtasq/gLe7cp5cuozehE6hN+E4sxCamRR/WDiHTg+f7ZIAS024r8UJQffY+e2gGELXQZgQoFDfNDCg==", + "dev": true, + "requires": { + "@webpack-contrib/schema-utils": "^1.0.0-beta.0", + "loader-utils": "^1.1.0", + "webpack-sources": "^1.1.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -10031,9 +10332,9 @@ } }, "mustache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.0.tgz", - "integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", + "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", "dev": true }, "mute-stream": { @@ -10563,9 +10864,9 @@ "dev": true }, "node-version": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.1.3.tgz", - "integrity": "sha512-rEwE51JWn0yN3Wl5BXeGn5d52OGbSXzWiiXRjAQeuyvcGKyvuSILW2rb3G7Xh+nexzLwhTpek6Ehxd6IjvHePg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", + "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==", "dev": true }, "nopt": { @@ -11592,6 +11893,58 @@ } } }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + } + } + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -12774,17 +13127,6 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "prerender-spa-plugin": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/prerender-spa-plugin/-/prerender-spa-plugin-3.2.1.tgz", - "integrity": "sha512-UHgpVAtnUP/usSnl3AnNjZRw8zEQzT4WmRWA7p2EqucgGYseDg9NAOyXPcUmMWdCx0UMYpetF36zyYtCF3AoBw==", - "dev": true, - "requires": { - "@prerenderer/prerenderer": "^0.7.1", - "@prerenderer/renderer-puppeteer": "^0.1.4", - "html-minifier": "^3.5.16" - } - }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", @@ -12819,12 +13161,6 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -12840,12 +13176,6 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, - "promise-limit": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.6.0.tgz", - "integrity": "sha512-ZyUYT6/nrx7oDInu6Go128Ck4N2aXqpV8G0QyU3ugGS0PRPOf5Y7v0IF0yZW2pwZCGtHzBftKievNj8491suJw==", - "dev": true - }, "promisify-event": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/promisify-event/-/promisify-event-1.0.0.tgz", @@ -12871,21 +13201,15 @@ "dev": true }, "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", "dev": true, "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.6.0" + "ipaddr.js": "1.8.0" } }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", - "dev": true - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -12956,30 +13280,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "puppeteer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.5.0.tgz", - "integrity": "sha512-eELwFtFxL+uhmg4jPZOZXzSrPEYy4CaYQNbcchBbfxY+KjMpnv6XGf/aYWaQG49OTpfi2/DMziXtDM8XuJgoUA==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", - "progress": "^2.0.0", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^5.1.1" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -13839,23 +14139,6 @@ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" }, - "resolve-pkg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-1.0.0.tgz", - "integrity": "sha1-4ZoV54rKLhJEYdySsuOUPvk0lNk=", - "dev": true, - "requires": { - "resolve-from": "^2.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", - "dev": true - } - } - }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -14710,6 +14993,12 @@ "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true }, + "spawn-args": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/spawn-args/-/spawn-args-0.2.0.tgz", + "integrity": "sha1-+30L0dcP1DFr2ePew4nmX51jYbs=", + "dev": true + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", @@ -15829,9 +16118,9 @@ } }, "testcafe": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-0.20.3.tgz", - "integrity": "sha512-wvxBspl5KSK6eEQdrjOfR4cyhot4XkDFfsGkldUy/yuSfczsc8qkte+TXPt8WWfQF/AnEsmtLVWFKCd4sB/DHg==", + "version": "0.20.5", + "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-0.20.5.tgz", + "integrity": "sha512-clCWZmR6OzsCS2LxkHnk4xaSgH9Lyw7PRgwJ4XJv5k2mchuj1qHQuLHj/lRYkciQlZt78V5/wHV+C3To6od4mw==", "dev": true, "requires": { "async-exit-hook": "^1.1.2", @@ -15857,6 +16146,7 @@ "error-stack-parser": "^1.3.6", "globby": "^3.0.1", "graceful-fs": "^4.1.11", + "gulp-data": "^1.3.1", "indent-string": "^1.2.2", "is-ci": "^1.0.10", "is-glob": "^2.0.1", @@ -15884,8 +16174,8 @@ "sanitize-filename": "^1.6.0", "source-map-support": "^0.5.5", "strip-bom": "^2.0.0", - "testcafe-browser-tools": "1.6.3", - "testcafe-hammerhead": "14.0.0", + "testcafe-browser-tools": "1.6.4", + "testcafe-hammerhead": "14.2.2", "testcafe-legacy-api": "3.1.7", "testcafe-reporter-json": "^2.1.0", "testcafe-reporter-list": "^2.1.0", @@ -16036,9 +16326,9 @@ } }, "testcafe-browser-tools": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-1.6.3.tgz", - "integrity": "sha512-nweLblMZEYX2rAJUX2Ju/TgGWZbWVo8I0SW/Pi7whcOPe+tduDBj+Z5NCOmpX6SyCz2iKbev31JLEioAr5q+8A==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-1.6.4.tgz", + "integrity": "sha512-I0PsEVT39C7MpjIDjJWKc7zPEl5GFAGgl/4eO8NISaJbITL0czeX9ITomMV6m3IZtk6WUWlIkOkl9R04GVa4fw==", "dev": true, "requires": { "array-find": "^1.0.0", @@ -16078,19 +16368,22 @@ } }, "testcafe-hammerhead": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-14.0.0.tgz", - "integrity": "sha512-2MSbTSGWSUFA9lw0J/fl3AceDOgF2Qw3Vfi6IwEXU1wr4ydxyfVEHmJpewnIVNcTBLmCHDnrm3xzvwFt4oOPtw==", + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-14.2.2.tgz", + "integrity": "sha512-n9vM5NeVbpv30L/73vKJe/O0AoBJWMfB9UVNAZOBuc7uleQ9jnHSaTXDOJjNO+5STVe3oPBqvQ7FDTX43m/c3A==", "dev": true, "requires": { "bowser": "1.6.0", "brotli": "^1.3.1", "crypto-md5": "^1.0.0", "css": "2.2.3", + "gulp-clone": "^2.0.1", + "gulp-run-command": "0.0.9", "iconv-lite": "0.4.11", "lodash": "4.17.10", "lru-cache": "2.6.3", "match-url-wildcard": "0.0.2", + "merge-stream": "^1.0.1", "mime": "~1.4.1", "mustache": "^2.1.1", "nanoid": "^0.2.2", @@ -16268,6 +16561,12 @@ "integrity": "sha1-5tZsVyzhWvJmcGrw/WELKoQd1EM=", "dev": true }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "throttleit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", @@ -16345,9 +16644,9 @@ "dev": true }, "time-limit-promise": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/time-limit-promise/-/time-limit-promise-1.0.3.tgz", - "integrity": "sha512-+0FBZPycPPgHi938ZUWrUo109SoiNm9Q2phunDBaf6Hq3RgALLTkSH5s6kM58+81obl3fNLpBozwvPw+m5MM4A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/time-limit-promise/-/time-limit-promise-1.0.4.tgz", + "integrity": "sha512-FLHDDsIDducw7MBcRWlFtW2Tm50DoKOSFf0Nzx17qwXj8REXCte0eUkHrJl9QU3Bl9arG3XNYX0PcHpZ9xyuLw==", "dev": true }, "time-stamp": { @@ -16362,6 +16661,12 @@ "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", "dev": true }, + "timeout-as-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/timeout-as-promise/-/timeout-as-promise-1.0.0.tgz", + "integrity": "sha1-c2foEfyZKs/Nzaq/LlDfr4shV28=", + "dev": true + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", @@ -17176,6 +17481,12 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=", + "dev": true + }, "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", @@ -17519,6 +17830,17 @@ "webpack-sources": "^1.0.1" } }, + "webpack-cdn-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-cdn-plugin/-/webpack-cdn-plugin-2.2.0.tgz", + "integrity": "sha512-gOhAB+JFD5GvRgYssPcSo+zq1Bcbs1ubNtyoBdhhOqw05djpG3R4vcbKbdy0dlj+iYZANM2NgoUBePII1VldUQ==", + "dev": true, + "requires": { + "babel-cli": "^6.26.0", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-preset-env": "^1.7.0" + } + }, "webpack-cli": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.0.8.tgz", @@ -17852,6 +18174,12 @@ "lodash": "^4.17.5" } }, + "webpack-node-externals": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", + "dev": true + }, "webpack-sources": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", @@ -18055,12 +18383,14 @@ } }, "ws": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.0.tgz", - "integrity": "sha512-c18dMeW+PEQdDFzkhDsnBAlS4Z8KGStBQQUcQ5mf7Nf689jyGk0594L+i9RaQuf4gog6SvWLJorz2NfSaqxZ7w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } }, "x-is-string": { diff --git a/package.json b/package.json index b416f0d..bd62c0e 100644 --- a/package.json +++ b/package.json @@ -16,15 +16,16 @@ "license": "MIT", "homepage": "https://sik.ayy.fi", "scripts": { - "build": "npm run clean-dist && webpack -p --config=configs/webpack/prod.js", - "clean-dist": "rm -f -r -d dist", + "build": "NODE_ENV=production npm-run-all build:client build:server", + "build:server": "webpack -p --config=configs/webpack/prod.js --env.platform=server", + "build:client": "webpack -p --config=configs/webpack/prod.js --env.platform=client", "lint": "npm run lint:ts && npm run lint:sass", "lint:ts": "tslint -p tsconfig.json --format stylish --exclude node_modules", "lint:ts:fix": "tslint -p tsconfig.json --format stylish --exclude node_modules --fix", "lint:sass": "stylelint ./src/**/**/*.scss ./src/**/*.scss ./src/*.scss", "start": "npm run start-dev", "start-dev": "webpack-dev-server --config=configs/webpack/dev.js", - "serve": "serve -p 3000 dist", + "serve": "node dist/js/server.js", "start-prod": "npm run build && npm run serve", "mock-backend": "json-server --watch db.json -p 1234", "test": "npm-run-all lint test:e2e", @@ -47,10 +48,11 @@ "babel-core": "^6.26.3", "babel-loader": "^7.1.4", "babel-preset-env": "^1.7.0", + "babel-preset-minify": "^0.4.3", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.11", "dotenv-webpack": "^1.5.7", - "dynamic-cdn-webpack-plugin": "^4.0.0-rc.1", + "express": "^4.16.3", "favicons-webpack-plugin": "0.0.9", "file-loader": "^1.1.11", "fs-extra": "^6.0.1", @@ -58,13 +60,13 @@ "husky": "^1.0.0-rc.9", "image-webpack-loader": "^4.3.0", "json-server": "^0.14.0", + "mini-css-extract-plugin": "^0.4.1", "module-to-cdn": "^3.1.2", + "morgan": "^1.9.0", "node-sass": "^4.9.0", "npm-run-all": "^4.1.3", "plop": "^2.0.0", "postcss-loader": "^2.1.5", - "prerender-spa-plugin": "^3.2.1", - "puppeteer": "^1.5.0", "react": "^16.4.0", "react-addons-test-utils": "^15.6.2", "react-dom": "^16.4.0", @@ -75,15 +77,17 @@ "stylelint": "^9.2.1", "stylelint-config-standard": "^18.2.0", "stylelint-webpack-plugin": "^0.10.5", - "testcafe": "^0.20.3", + "testcafe": "^0.20.5", "testcafe-react-selectors": "^2.1.0", "tslint": "^5.10.0", "uglifyjs-webpack-plugin": "^1.2.5", "webpack": "^4.11.1", + "webpack-cdn-plugin": "^2.2.0", "webpack-cli": "^3.0.3", "webpack-dev-middleware": "^3.1.3", "webpack-dev-server": "^3.1.4", - "webpack-merge": "^4.1.2" + "webpack-merge": "^4.1.2", + "webpack-node-externals": "^1.7.2" }, "dependencies": { "axios": "^0.18.0", diff --git a/src/client/App.tsx b/src/client/App.tsx new file mode 100644 index 0000000..237bca9 --- /dev/null +++ b/src/client/App.tsx @@ -0,0 +1,9 @@ +import * as React from "react"; +import { BrowserRouter } from "react-router-dom"; +import Routes from "../routes"; + +export default () => ( + + + +); diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 0000000..a985923 --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,7 @@ +import * as React from "react"; +import { hydrate } from "react-dom"; +import App from "./App"; + +const elem = document.getElementById("root"); + +hydrate(React.createElement(App), elem); diff --git a/src/index.scss b/src/index.scss index 2fc91f6..7f56439 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,3 +1,4 @@ +@import "./assets/scss/normalize"; @import "./assets/scss/globals"; html, @@ -19,7 +20,7 @@ body { body { max-width: 1200px; padding: 0 0.5rem; - margin: auto; + margin: auto !important; } h1 { diff --git a/src/index.tsx b/src/index.tsx index e24297c..b57b146 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,9 +3,7 @@ import {render} from "react-dom"; import { BrowserRouter } from "react-router-dom"; import {AppContainer} from "react-hot-loader"; import Routes from "./routes"; -import "normalize.css"; import "./index.scss"; -import "./index.d.ts"; const rootEl = document.getElementById("root"); diff --git a/src/routes.tsx b/src/routes.tsx index 0ff8db0..0ea8ce7 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -3,6 +3,7 @@ import { Switch, Route } from "react-router-dom"; import FrontPage from "./pages/FrontPage"; import NotFoundPage from "./pages/NotFoundPage"; import CommonPage from "./pages/CommonPage"; +import "./index.scss"; const Routes = () => ( diff --git a/src/server/App.tsx b/src/server/App.tsx new file mode 100644 index 0000000..2a0e05b --- /dev/null +++ b/src/server/App.tsx @@ -0,0 +1,9 @@ +import * as React from "react"; +import { StaticRouter } from "react-router-dom"; +import Routes from "../routes"; + +export default ({ url }) => ( + + + +); diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..f5db3aa --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,27 @@ +import * as React from "react"; +import * as express from "express"; +import { renderToString } from "react-dom/server"; +import * as morgan from "morgan"; + +import App from "./App"; +import * as fs from "fs"; +import * as path from "path"; + +const port = 3000; +const server = express(); + +const indexHtml = fs.readFileSync(path.resolve("./dist/index.html"), "utf-8"); +const html = body => indexHtml.replace("
", `
${body}
`); + +server.use(morgan("short")); +server.use("/assets", express.static("dist/assets")); +server.use("/js", express.static("dist/js")); + +server.get("*", (req, res) => { + const result = renderToString(React.createElement(App, { url: req.url })); + res.send( + html(result) + ); +}); + +server.listen(3000, () => console.log("React SSR express server listening on port 3000!"));