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"],
"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/
prerender/
# Coverage reports
coverage/

View File

@ -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"
},

View File

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

View File

@ -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(
<RavenBoundary>
<App/>
<Component/>
</RavenBoundary>,
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 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: {

View File

@ -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)
]
}
}
];

View File

@ -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"