Reworking static page generation

Including styles in components wasn't working with the old system.
This commit is contained in:
Jeff Avallone 2018-02-13 17:10:32 -05:00
parent ad6583d5dc
commit 7238643740
13 changed files with 111 additions and 44 deletions

View File

@ -1,4 +1,4 @@
{ {
"presets": ["env", "react"], "presets": ["env", "react"],
"plugins": ["transform-class-properties"] "plugins": ["transform-class-properties", "syntax-dynamic-import"]
} }

1
.gitignore vendored
View File

@ -20,6 +20,7 @@ node_modules/
# Build output # Build output
build/ build/
prerender/
# Coverage reports # Coverage reports
coverage/ coverage/

View File

@ -13,7 +13,9 @@
"start": "webpack-dev-server --config webpack.dev.js", "start": "webpack-dev-server --config webpack.dev.js",
"start:prod": "run-s build start:http-server", "start:prod": "run-s build start:http-server",
"start:http-server": "http-server ./build", "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": "run-s test:lint 'test:unit --coverage'",
"test:unit": "jest", "test:unit": "jest",
"test:lint": "eslint --ignore-path .gitignore .", "test:lint": "eslint --ignore-path .gitignore .",
@ -49,10 +51,12 @@
"babel-eslint": "^8.2.1", "babel-eslint": "^8.2.1",
"babel-jest": "^22.2.2", "babel-jest": "^22.2.2",
"babel-loader": "^7.1.2", "babel-loader": "^7.1.2",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-register": "^6.26.0", "babel-register": "^6.26.0",
"cheerio": "^1.0.0-rc.2",
"copy-webpack-plugin": "^4.4.1", "copy-webpack-plugin": "^4.4.1",
"css-loader": "^0.28.9", "css-loader": "^0.28.9",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",
@ -82,6 +86,7 @@
"uglifyjs-webpack-plugin": "^1.1.8", "uglifyjs-webpack-plugin": "^1.1.8",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-merge": "^4.1.1", "webpack-merge": "^4.1.1",
"webpack-node-externals": "^1.6.0",
"workbox-webpack-plugin": "^2.1.2", "workbox-webpack-plugin": "^2.1.2",
"yaml-loader": "^0.5.0" "yaml-loader": "^0.5.0"
}, },

View File

@ -1,15 +1,11 @@
import React from 'react'; import React from 'react';
import Header from './Header';
import Footer from './Footer';
import Message from './Message'; import Message from './Message';
const App = () => <React.Fragment> const App = () => (
<Header/>
<Message heading="React App"> <Message heading="React App">
<p>Placeholder app content</p> <p>Placeholder app content</p>
</Message> </Message>
<Footer/> );
</React.Fragment>;
export default App; export default App;

View File

@ -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(
<PageTemplate>
<Component/>
</PageTemplate>
);

View File

@ -1,16 +1,13 @@
import 'babel-register';
import React from 'react'; import React from 'react';
import '../../i18n';
import PageTemplate, { renderToString } from '../../components/PageTemplate';
import Message from '../../components/Message'; import Message from '../../components/Message';
import AlertIcon from 'feather-icons/dist/icons/alert-octagon.svg'; import AlertIcon from 'feather-icons/dist/icons/alert-octagon.svg';
import Header from '../../components/Header'; import Header from '../../components/Header';
import Footer from '../../components/Footer'; import Footer from '../../components/Footer';
import App from '../../components/App';
export default renderToString( const Component = () => (
<PageTemplate> <React.Fragment>
<Header/> <Header/>
<noscript> <noscript>
<Message className="error" icon={ AlertIcon } heading="JavaScript Required"> <Message className="error" icon={ AlertIcon } heading="JavaScript Required">
@ -23,6 +20,9 @@ export default renderToString(
<p>Most popular ad blockers will prevent these tools from sending any tracking data, and doing so will <b>not</b> impact the performance of this app. Regexper is not supported by ad revenue or sales of any kind. The information collected by these tools is used to monitor application performance, determine browser support, and collect error reports.</p> <p>Most popular ad blockers will prevent these tools from sending any tracking data, and doing so will <b>not</b> impact the performance of this app. Regexper is not supported by ad revenue or sales of any kind. The information collected by these tools is used to monitor application performance, determine browser support, and collect error reports.</p>
</Message> </Message>
</noscript> </noscript>
<App/>
<Footer/> <Footer/>
</PageTemplate> </React.Fragment>
); );
export default Component;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from '../../components/App'; import Component from './Component';
import RavenBoundary from '../../components/RavenBoundary'; import RavenBoundary from '../../components/RavenBoundary';
import '../../style.css'; import '../../style.css';
@ -20,7 +20,7 @@ try {
ReactDOM.render( ReactDOM.render(
<RavenBoundary> <RavenBoundary>
<App/> <Component/>
</RavenBoundary>, </RavenBoundary>,
document.getElementById('root')); document.getElementById('root'));
} }

20
src/prerender.js Normal file
View File

@ -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(<Component/>));
fs.writeFileSync(pagePath, markup.html());
});
});

15
src/template.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content={ pkg.description } />
<link rel="author" href="/humans.txt" />
<title>Regexper</title>
</head>
<body data-build-id="<%= process.env.BUILD_ID %>">
<div id="root"></div>
</body>
</html>

View File

@ -10,7 +10,7 @@ const pkg = require('./package.json');
const pages = fs.readdirSync(path.resolve(__dirname, 'src/pages')); const pages = fs.readdirSync(path.resolve(__dirname, 'src/pages'));
const pagePlugins = pages.map(name => new HtmlPlugin({ const pagePlugins = pages.map(name => new HtmlPlugin({
template: `./src/pages/${ name }/template.js`, template: './src/template.html',
filename: `${ name }.html`, filename: `${ name }.html`,
chunks: ['common', name], chunks: ['common', name],
minify: { minify: {
@ -28,7 +28,7 @@ const pagePlugins = pages.map(name => new HtmlPlugin({
module.exports = { module.exports = {
entry: pages.reduce((pages, name) => { entry: pages.reduce((pages, name) => {
pages[name] = `./src/pages/${ name }`; pages[name] = `./src/pages/${ name }/browser`;
return pages; return pages;
}, {}), }, {}),
output: { output: {

View File

@ -1,9 +1,14 @@
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const common = require('./webpack.common.js'); const common = require('./webpack.common.js');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin'); const WorkboxPlugin = require('workbox-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
module.exports = merge(common, { module.exports = [
// Web
merge(common, {
devtool: 'source-map', devtool: 'source-map',
plugins: [ plugins: [
new UglifyJSPlugin({ new UglifyJSPlugin({
@ -14,4 +19,34 @@ module.exports = merge(common, {
skipWaiting: 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)
]
}
}
];

View File

@ -619,6 +619,10 @@ babel-plugin-syntax-class-properties@^6.8.0:
version "6.13.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" 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: babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.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" 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: dependencies:
lodash "^4.17.4" 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: webpack-sources@^1.0.1, webpack-sources@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"