diff --git a/.babelrc b/.babelrc
index 59bce79..0c97598 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,4 +1,4 @@
{
"presets": ["env", "react"],
- "plugins": ["transform-class-properties"]
+ "plugins": ["transform-class-properties", "syntax-dynamic-import"]
}
diff --git a/.gitignore b/.gitignore
index 131321b..a076b55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ node_modules/
# Build output
build/
+prerender/
# Coverage reports
coverage/
diff --git a/package.json b/package.json
index 4a9e96f..3f8914d 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,9 @@
"start": "webpack-dev-server --config webpack.dev.js",
"start:prod": "run-s build start:http-server",
"start:http-server": "http-server ./build",
- "build": "webpack --config webpack.prod.js",
+ "build": "run-s build:webpack build:prerender",
+ "build:webpack": "webpack --config webpack.prod.js",
+ "build:prerender": "node ./prerender/prerender.js",
"test": "run-s test:lint 'test:unit --coverage'",
"test:unit": "jest",
"test:lint": "eslint --ignore-path .gitignore .",
@@ -49,10 +51,12 @@
"babel-eslint": "^8.2.1",
"babel-jest": "^22.2.2",
"babel-loader": "^7.1.2",
+ "babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-register": "^6.26.0",
+ "cheerio": "^1.0.0-rc.2",
"copy-webpack-plugin": "^4.4.1",
"css-loader": "^0.28.9",
"enzyme": "^3.3.0",
@@ -82,6 +86,7 @@
"uglifyjs-webpack-plugin": "^1.1.8",
"webpack": "^3.10.0",
"webpack-merge": "^4.1.1",
+ "webpack-node-externals": "^1.6.0",
"workbox-webpack-plugin": "^2.1.2",
"yaml-loader": "^0.5.0"
},
diff --git a/src/components/App.js b/src/components/App.js
index 5efe890..4c4b404 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -1,15 +1,11 @@
import React from 'react';
-import Header from './Header';
-import Footer from './Footer';
import Message from './Message';
-const App = () =>
-
+const App = () => (
Placeholder app content
-
-;
+);
export default App;
diff --git a/src/pages/404/index.js b/src/pages/404/browser.js
similarity index 100%
rename from src/pages/404/index.js
rename to src/pages/404/browser.js
diff --git a/src/pages/404/template.js b/src/pages/404/template.js
deleted file mode 100644
index c64ec02..0000000
--- a/src/pages/404/template.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import 'babel-register';
-import React from 'react';
-
-import '../../i18n';
-
-import PageTemplate, { renderToString } from '../../components/PageTemplate';
-import Component from './Component';
-
-export default renderToString(
-
-
-
-);
diff --git a/src/pages/index/template.js b/src/pages/index/Component.js
similarity index 87%
rename from src/pages/index/template.js
rename to src/pages/index/Component.js
index 75ed0f9..fbc3a64 100644
--- a/src/pages/index/template.js
+++ b/src/pages/index/Component.js
@@ -1,16 +1,13 @@
-import 'babel-register';
import React from 'react';
-import '../../i18n';
-
-import PageTemplate, { renderToString } from '../../components/PageTemplate';
import Message from '../../components/Message';
import AlertIcon from 'feather-icons/dist/icons/alert-octagon.svg';
import Header from '../../components/Header';
import Footer from '../../components/Footer';
+import App from '../../components/App';
-export default renderToString(
-
+const Component = () => (
+
+
-
+
);
+
+export default Component;
diff --git a/src/pages/index/index.js b/src/pages/index/browser.js
similarity index 91%
rename from src/pages/index/index.js
rename to src/pages/index/browser.js
index 2152f1c..9fab7fc 100644
--- a/src/pages/index/index.js
+++ b/src/pages/index/browser.js
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import App from '../../components/App';
+import Component from './Component';
import RavenBoundary from '../../components/RavenBoundary';
import '../../style.css';
@@ -20,7 +20,7 @@ try {
ReactDOM.render(
-
+
,
document.getElementById('root'));
}
diff --git a/src/prerender.js b/src/prerender.js
new file mode 100644
index 0000000..d92d29e
--- /dev/null
+++ b/src/prerender.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+import fs from 'fs';
+import cheerio from 'cheerio';
+
+import './i18n';
+
+const pages = fs.readdirSync('./src/pages');
+
+pages.forEach(page => {
+ import(`./pages/${ page }/Component`).then(component => {
+ const Component = component.default;
+ const pagePath = `./build/${ page }.html`;
+
+ const markup = cheerio.load(fs.readFileSync(pagePath));
+
+ markup('#root').html(renderToString());
+ fs.writeFileSync(pagePath, markup.html());
+ });
+});
diff --git a/src/template.html b/src/template.html
new file mode 100644
index 0000000..9c0e4e7
--- /dev/null
+++ b/src/template.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+ Regexper
+
+
+
+
+
diff --git a/webpack.common.js b/webpack.common.js
index e60b8c7..88cf3fd 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -10,7 +10,7 @@ const pkg = require('./package.json');
const pages = fs.readdirSync(path.resolve(__dirname, 'src/pages'));
const pagePlugins = pages.map(name => new HtmlPlugin({
- template: `./src/pages/${ name }/template.js`,
+ template: './src/template.html',
filename: `${ name }.html`,
chunks: ['common', name],
minify: {
@@ -28,7 +28,7 @@ const pagePlugins = pages.map(name => new HtmlPlugin({
module.exports = {
entry: pages.reduce((pages, name) => {
- pages[name] = `./src/pages/${ name }`;
+ pages[name] = `./src/pages/${ name }/browser`;
return pages;
}, {}),
output: {
diff --git a/webpack.prod.js b/webpack.prod.js
index 30c5a29..9942ffb 100644
--- a/webpack.prod.js
+++ b/webpack.prod.js
@@ -1,17 +1,52 @@
+const path = require('path');
+const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
+const nodeExternals = require('webpack-node-externals');
-module.exports = merge(common, {
- devtool: 'source-map',
- plugins: [
- new UglifyJSPlugin({
- sourceMap: true
- }),
- new WorkboxPlugin({
- clientsClaim: true,
- skipWaiting: true
- })
- ]
-});
+module.exports = [
+ // Web
+ merge(common, {
+ devtool: 'source-map',
+ plugins: [
+ new UglifyJSPlugin({
+ sourceMap: true
+ }),
+ new WorkboxPlugin({
+ clientsClaim: true,
+ skipWaiting: true
+ })
+ ]
+ }),
+ // Node (prerender)
+ {
+ target: 'node',
+ externals: [nodeExternals({
+ whitelist: [ /\.svg$/ ]
+ })],
+ entry: {
+ prerender: './src/prerender.js'
+ },
+ output: {
+ filename: '[name].js',
+ chunkFilename: '[name].chunk.js',
+ path: path.resolve(__dirname, 'prerender')
+ },
+ plugins: [
+ // Only want the EnvironmentPlugin
+ common.plugins.find(plugin => plugin instanceof webpack.EnvironmentPlugin)
+ ],
+ module: {
+ rules: [
+ // Replace the rule for CSS files
+ {
+ test: /\.css$/,
+ loader: 'css-loader/locals'
+ },
+ ...common.module.rules.filter(rule => !rule.oneOf)
+ ]
+ }
+ }
+];
diff --git a/yarn.lock b/yarn.lock
index c12b12a..9b07ba8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -619,6 +619,10 @@ babel-plugin-syntax-class-properties@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
+babel-plugin-syntax-dynamic-import@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da"
+
babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
@@ -8200,6 +8204,10 @@ webpack-merge@^4.1.1:
dependencies:
lodash "^4.17.4"
+webpack-node-externals@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz#232c62ec6092b100635a3d29d83c1747128df9bd"
+
webpack-sources@^1.0.1, webpack-sources@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"