Skip to content

Commit

Permalink
Added multiroute prerendering (#192)
Browse files Browse the repository at this point in the history
* Added multiroute prerendering

* Added documentation for prerendering multiple routes

* Fixed missing semicolon
  • Loading branch information
framp authored and rkostrzewski committed Jul 5, 2017
1 parent 136f5ae commit db1641e
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 15 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ $ preact build
--less, -l Build and compile LESS files. [default: false]
--sass, -s Build and compile SASS files. [default: false]
--prerender Pre-render static app content. [default: true]
--prerenderUrls Path to pre-render routes configuration. [default "prerender-urls.json"]
--template Path to template file.
--clean Clear output directory before building. [default: true]
--json Generate build statistics for analysis. [default: false]
Expand Down Expand Up @@ -144,6 +145,26 @@ export default function (config, env, helpers) {
```
See [WebpackConfigHelpers] docs for more info on ```helpers``` argument.

#### Prerender multiple routes

The `--prerender` flag will prerender by default only the root of your application.
If you want to prerender other routes you can create a `prerender-urls.json` file, which contains the set of routes you want to render.
The format required for defining your routes is an array of objects with a `url` key and an optional `title` key.
```js
// prerender-urls.json
[{
"url": "/",
"title": "Homepage"
}, {
"url": "/route/random"
}]
```

You can customise the path of `prerender-urls.json` by using the flag `--prerenderUrls`.
```
preact build --prerenderUrls src/prerender-urls.json
```

#### Template
A template is used to render your page.

Expand Down
4 changes: 4 additions & 0 deletions src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export default asyncCommand({
description: 'Pre-render static app content.',
default: true
},
prerenderUrls: {
description: 'Path to pre-render routes configuration.',
default: 'prerender-urls.json'
},
clean: {
description: 'Clear output directory before building.',
default: true
Expand Down
2 changes: 1 addition & 1 deletion src/lib/webpack/webpack-base-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function exists(file) {
return false;
}

function readJson(file) {
export function readJson(file) {
if (file in readJson.cache) return readJson.cache[file];
let ret;
try { ret = JSON.parse(readFileSync(file)); }
Expand Down
31 changes: 18 additions & 13 deletions src/lib/webpack/webpack-client-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ScriptExtHtmlWebpackPlugin from 'script-ext-html-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import SWPrecacheWebpackPlugin from 'sw-precache-webpack-plugin';
import PushManifestPlugin from './push-manifest';
import baseConfig, { exists, helpers } from './webpack-base-config';
import baseConfig, { exists, readJson, helpers } from './webpack-base-config';
import prerender from './prerender';

export default env => {
Expand Down Expand Up @@ -164,9 +164,9 @@ const production = config => addPlugins([
})
]);

const htmlPlugin = (config, outputDir) => addPlugins([
new HtmlWebpackPlugin({
filename: 'index.html',
const htmlPlugin = (config, outputDir) => {
const htmlWebpackConfig = ({ url, title }) => ({
filename: resolve(outputDir, url.substring(1), 'index.html'),
template: `!!ejs-loader!${config.template || resolve(__dirname, '../../resources/template.html')}`,
minify: config.production && {
collapseWhitespace: true,
Expand All @@ -180,16 +180,21 @@ const htmlPlugin = (config, outputDir) => addPlugins([
inject: true,
compile: true,
preload: config.preload===true,
title: config.title || config.manifest.name || config.manifest.short_name || (config.pkg.name || '').replace(/^@[a-z]\//, '') || 'Preact App',
title: title || config.title || config.manifest.name || config.manifest.short_name || (config.pkg.name || '').replace(/^@[a-z]\//, '') || 'Preact App',
excludeAssets: [/(bundle|polyfills)(\..*)?\.js$/],
config,
ssr(params) {
return config.prerender ? prerender(outputDir, params) : '';
return config.prerender ? prerender(outputDir, { ...params, url }) : '';
}
}),
new HtmlWebpackExcludeAssetsPlugin(),
new ScriptExtHtmlWebpackPlugin({
// inline: 'bundle.js',
defaultAttribute: 'defer'
})
]);
});
const pages = readJson(resolve(config.cwd, config.prerenderUrls || '')) || [{ url: "/" }];
return addPlugins(pages
.map((page) => new HtmlWebpackPlugin(htmlWebpackConfig(page)))
.concat([
new HtmlWebpackExcludeAssetsPlugin(),
new ScriptExtHtmlWebpackPlugin({
// inline: 'bundle.js',
defaultAttribute: 'defer'
})
]));
};
20 changes: 20 additions & 0 deletions tests/build.snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,23 @@ export const withCustomTemplate = `
</body>
</html>
`;

export const multiplePrerenderingHome = `
<body>
<div id="app">
<div>Home</div>
</div>
<script defer="defer" src="/bundle.js"></script>
{{ ... }}
</body>
`;

export const multiplePrerenderingRoute = `
<body>
<div id="app">
<div>Route66</div>
</div>
<script defer="defer" src="/bundle.js"></script>
{{ ... }}
</body>
`;
14 changes: 13 additions & 1 deletion tests/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import htmlLooksLike from 'html-looks-like';
import { create, build } from './lib/cli';
import lsr from './lib/lsr';
import { setup, fromSubject } from './lib/output';
import expectedOutputs, { sassPrerendered, withCustomTemplate } from './build.snapshot';
import expectedOutputs, { sassPrerendered, withCustomTemplate, multiplePrerenderingHome, multiplePrerenderingRoute } from './build.snapshot';
import filesMatchSnapshot from './lib/filesMatchSnapshot';

describe('preact build', () => {
Expand Down Expand Up @@ -40,6 +40,18 @@ describe('preact build', () => {
expect(async () => await build(app)).not;
});

it(`should prerender the routes provided with prerenderUrls.`, async () => {
let app = await fromSubject('multiple-prerendering');
await build(app);

let output = await fs.readFile(resolve(app, './build/index.html'), 'utf-8');
let html = output.match(/<body>.*<\/body>/)[0];
htmlLooksLike(html, multiplePrerenderingHome);
output = await fs.readFile(resolve(app, './build/route66/index.html'), 'utf-8');
html = output.match(/<body>.*<\/body>/)[0];
htmlLooksLike(html, multiplePrerenderingRoute);
});

it(`should use custom preact.config.js.`, async () => {
// app with custom template set via preact.config.js
let app = await fromSubject('custom-webpack');
Expand Down
23 changes: 23 additions & 0 deletions tests/subjects/multiple-prerendering/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { h, Component } from 'preact';
import { Router } from 'preact-router';

const Home = () => <div>Home</div>;

const Route66 = () => <div>Route66</div>;

export default class App extends Component {
handleRoute = e => {
this.currentUrl = e.url;
};

render() {
return (
<div id="app">
<Router onChange={this.handleRoute}>
<Home path="/" />
<Route66 path="/route66" />
</Router>
</div>
);
}
}
16 changes: 16 additions & 0 deletions tests/subjects/multiple-prerendering/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "preact-app",
"version": "0.0.1",
"private": true,
"dependencies": {
"preact": "latest",
"preact-compat": "latest",
"preact-router": "latest"
},
"devDependencies": {
"eslint": "^4.1.1",
"eslint-config-synacor": "^1.0.1",
"if-env": "^1.0.0",
"preact-cli": "file:../../../"
}
}
1 change: 1 addition & 0 deletions tests/subjects/multiple-prerendering/prerender-urls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{ "url": "/" }, { "url": "/route66", "title": "Route66"}]

0 comments on commit db1641e

Please sign in to comment.