Skip to content

Commit 7d57ecf

Browse files
committed
add basic setup for static docs
1 parent a84359a commit 7d57ecf

14 files changed

+337
-1
lines changed

.babelrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"env": {
44
"development": {
55
"presets": ["react-hmre"]
6-
}
6+
}
77
}
88
}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ bundle.js
1313
# NPM debug
1414
npm-debug.log
1515
npm-debug.log*
16+
17+
docs/public

docs/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Docs
2+
3+
A boilerplate for building static sites with [React][] and [React Router][]
4+
5+
**Quick Start:**
6+
7+
* `npm install`
8+
* `npm start` to run a dev server
9+
* Write an awesome client-side app...
10+
* `npm run build` to minify, package and generate static HTML files from your routes
11+
12+
## Development
13+
14+
**Iportant Note:** This setup does not yet support generating static sites for dynamic routes such as `post/:id`. For more info see [this issue on the react-static-webpack-plugin repo](https://github.com/iansinnott/react-static-webpack-plugin/issues/2).

docs/client/components/About/index.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import styles from './styles.css';
3+
4+
const About = () => (
5+
<div className={ styles.root }>About :)</div>
6+
);
7+
8+
export default About;
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.root {
2+
background: grey;
3+
}

docs/client/components/App/index.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, { Component } from 'react';
2+
import { Link, IndexLink } from 'react-router';
3+
import 'normalize.css';
4+
5+
export default class App extends Component {
6+
render() {
7+
return (
8+
<div>
9+
<nav>
10+
<IndexLink to="/" activeClassName={'wow'}>Home</IndexLink>
11+
<Link to="/about" activeClassName={'that'}>About</Link>
12+
</nav>
13+
{this.props.children}
14+
</div>
15+
);
16+
}
17+
}

docs/client/components/Home/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react';
2+
3+
const Home = () => (
4+
<div>Home :)</div>
5+
);
6+
7+
export default Home;
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react';
2+
3+
const NotFound = () => (
4+
<div>Page not Found :(</div>
5+
);
6+
7+
export default NotFound;

docs/client/index.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { render } from 'react-dom';
3+
import { Router } from 'react-router';
4+
5+
// Since we're rendering static files don't forget to use browser history.
6+
// Server's don't get the URL hash during a request.
7+
import createBrowserHistory from 'history/lib/createBrowserHistory';
8+
9+
// Import your routes so that you can pass them to the <Router /> component
10+
import routes from './routes.js';
11+
12+
// Only render in the browser
13+
if (typeof document !== 'undefined') {
14+
render(
15+
<Router routes={routes} history={createBrowserHistory()} />,
16+
document.getElementById('root')
17+
);
18+
}
19+
20+
// Export the routes here so that ReactStaticPlugin can access them and build
21+
// the static files.
22+
export * from './routes.js';

docs/client/routes.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { Route, IndexRoute } from 'react-router';
3+
4+
import App from './components/App';
5+
import NotFound from './components/NotFound';
6+
import Home from './components/Home';
7+
import About from './components/About';
8+
9+
export const routes = (
10+
<Route path="/" title="App" component={App}>
11+
<IndexRoute component={Home} />
12+
<Route path="about" title="App - About" component={About} />
13+
<Route path="*" title="404: Not Found" component={NotFound} />
14+
</Route>
15+
);
16+
17+
export default routes;

docs/package.json

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "docs",
3+
"version": "0.0.1",
4+
"description": "docs",
5+
"scripts": {
6+
"clean": "rimraf public",
7+
"start:dev": "babel-node ./server.js",
8+
"start": "npm run start:dev",
9+
"build:static": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
10+
"build": "npm run clean && npm run build:static"
11+
},
12+
"license": "MIT",
13+
"devDependencies": {
14+
"autoprefixer-loader": "^3.1.0",
15+
"babel-cli": "^6.5.1",
16+
"babel-core": "^6.5.1",
17+
"babel-eslint": "^4.1.6",
18+
"babel-loader": "^6.2.2",
19+
"babel-plugin-react-transform": "^2.0.0",
20+
"babel-preset-es2015": "^6.5.0",
21+
"babel-preset-react": "^6.5.0",
22+
"babel-preset-react-hmre": "^1.1.0",
23+
"cross-env": "^1.0.7",
24+
"css-loader": "^0.23.0",
25+
"express": "^4.13.3",
26+
"extract-text-webpack-plugin": "^1.0.1",
27+
"file-loader": "^0.8.5",
28+
"history": "^2.0.0",
29+
"normalize.css": "^3.0.3",
30+
"react": "^15.0.0-rc.1",
31+
"react-dom": "^15.0.0-rc.1",
32+
"react-router": "^1.0.3",
33+
"react-static-webpack-plugin": "^0.4.0",
34+
"react-transform-catch-errors": "^1.0.0",
35+
"react-transform-hmr": "^1.0.1",
36+
"redbox-react": "^1.2.0",
37+
"rimraf": "^2.4.4",
38+
"style-loader": "^0.13.0",
39+
"url-loader": "^0.5.7",
40+
"webpack": "^1.12.9",
41+
"webpack-dev-middleware": "^1.4.0",
42+
"webpack-hot-middleware": "^2.6.0"
43+
},
44+
"dependencies": {}
45+
}

docs/server.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* NOTE: This file must be run with babel-node as Node is not yet compatible
3+
* with all of ES6 and we also use JSX.
4+
*/
5+
import url from 'url';
6+
import React from 'react';
7+
import { renderToStaticMarkup } from 'react-dom/server';
8+
import express from 'express';
9+
import webpack from 'webpack';
10+
11+
import config from './webpack.config.dev.js';
12+
13+
const Html = ({
14+
title = 'Rainbow Unicorns',
15+
bundle = '/app.js',
16+
body = '',
17+
stylesheet = '',
18+
}) => (
19+
<html lang="en">
20+
<head>
21+
<meta charSet="utf-8" />
22+
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
23+
<meta name="viewport" content="width=device-width, initial-scale=1" />
24+
<title>{title}</title>
25+
{stylesheet && <link rel="stylesheet" href={stylesheet} />}
26+
</head>
27+
<body>
28+
<div id="root" dangerouslySetInnerHTML={{ __html: body }} />
29+
<script src={bundle} />
30+
</body>
31+
</html>
32+
);
33+
34+
/**
35+
* Render the entire web page to a string. We use render to static markup here
36+
* to avoid react hooking on to the document HTML that will not be managed by
37+
* React. The body prop is a string that contains the actual document body,
38+
* which react will hook on to.
39+
*
40+
* We also take this opportunity to prepend the doctype string onto the
41+
* document.
42+
*
43+
* @param {object} props
44+
* @return {string}
45+
*/
46+
const renderDocumentToString = props => {
47+
const markup = renderToStaticMarkup(<Html {...props} />);
48+
return `<!doctype html>${markup}`;
49+
};
50+
51+
const app = express();
52+
const compiler = webpack(config);
53+
54+
app.use(require('webpack-dev-middleware')(compiler, {
55+
noInfo: true,
56+
publicPath: config.output.publicPath,
57+
}));
58+
59+
app.use(require('webpack-hot-middleware')(compiler));
60+
61+
// Send the boilerplate HTML payload down for all get requests. Routing will be
62+
// handled entirely client side and we don't make an effort to pre-render pages
63+
// before they are served when in dev mode.
64+
app.get('*', (req, res) => {
65+
const html = renderDocumentToString({
66+
bundle: `${config.output.publicPath}app.js`,
67+
});
68+
res.send(html);
69+
});
70+
71+
// NOTE: url.parse can't handle URLs without a protocol explicitly defined. So
72+
// if we parse '//localhost:8888' it doesn't work. We manually add a protocol even
73+
// though we are only interested in the port.
74+
const { port } = url.parse(`http:${config.output.publicPath}`);
75+
76+
app.listen(port, 'localhost', err => {
77+
if (err) return console.error(err); // eslint-disable-line no-console
78+
console.log(`Dev server listening at http://localhost:${port}`); // eslint-disable-line no-console
79+
80+
return undefined;
81+
});

docs/webpack.config.dev.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* eslint-disable no-var */
2+
var path = require('path');
3+
var webpack = require('webpack');
4+
5+
// Set up dev host host and HMR host. For the dev host this is pretty self
6+
// explanatory: We use a different live-reload server to server our static JS
7+
// files in dev, so we need to be able to actually point a script tag to that
8+
// host so it can load the right files. The HRM host is a bit stranger. For more
9+
// details on why we need this URL see the readme and:
10+
// https://github.com/glenjamin/webpack-hot-middleware/issues/37
11+
var DEV_PORT = process.env.DEV_PORT || 3000;
12+
var DEV_HOST = `//localhost:${DEV_PORT}/`;
13+
var HMR_HOST = `${DEV_HOST}__webpack_hmr`;
14+
15+
module.exports = {
16+
devtool: 'inline-source-map',
17+
18+
entry: {
19+
app: [
20+
`webpack-hot-middleware/client?path=${HMR_HOST}`,
21+
'./client/index.js',
22+
],
23+
},
24+
25+
output: {
26+
path: path.join(__dirname, 'public'),
27+
filename: '[name].js',
28+
publicPath: DEV_HOST,
29+
},
30+
31+
plugins: [
32+
new webpack.HotModuleReplacementPlugin(),
33+
new webpack.NoErrorsPlugin(),
34+
],
35+
36+
module: {
37+
loaders: [
38+
{
39+
test: /\.js$/,
40+
loaders: ['babel'],
41+
include: path.join(__dirname, 'client'),
42+
},
43+
{
44+
test: /\.css$/,
45+
loaders: ['style', 'css?modules&importLoaders=1&localIdentName=[local]___[hash:base64:5]'],
46+
},
47+
{
48+
test: /\.(png|jpg|gif|ico)$/,
49+
loaders: ['file?name=[name].[ext]'],
50+
},
51+
],
52+
},
53+
};

docs/webpack.config.prod.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* eslint-disable no-var */
2+
var path = require('path');
3+
var webpack = require('webpack');
4+
var ExtractTextPlugin = require('extract-text-webpack-plugin');
5+
var StaticSitePlugin = require('react-static-webpack-plugin');
6+
7+
module.exports = {
8+
devtool: 'source-map',
9+
10+
entry: {
11+
app: ['./client/index.js'],
12+
},
13+
14+
output: {
15+
path: path.join(__dirname, 'public'),
16+
filename: '[name].js',
17+
libraryTarget: 'umd',
18+
publicPath: '/',
19+
},
20+
21+
plugins: [
22+
new ExtractTextPlugin('[name].css', { allChunks: true }),
23+
new webpack.optimize.OccurenceOrderPlugin(),
24+
new webpack.DefinePlugin({
25+
'process.env': {
26+
NODE_ENV: JSON.stringify('production'),
27+
},
28+
}),
29+
new webpack.optimize.UglifyJsPlugin({
30+
screw_ie8: true,
31+
compressor: { warnings: false },
32+
}),
33+
new StaticSitePlugin({
34+
src: 'app',
35+
stylesheet: '/app.css',
36+
favicon: '/favicon.ico',
37+
}),
38+
],
39+
40+
module: {
41+
loaders: [
42+
{
43+
test: /\.js$/,
44+
loaders: ['babel'],
45+
include: path.join(__dirname, 'client'),
46+
},
47+
{
48+
test: /\.css$/,
49+
loader: ExtractTextPlugin.extract(
50+
'style',
51+
'css?modules&importLoaders=1&localIdentName=[local]___[hash:base64:5]'
52+
),
53+
},
54+
{
55+
test: /\.(png|jpg|gif|ico)$/,
56+
loaders: ['file?name=[name].[ext]'],
57+
},
58+
],
59+
},
60+
};

0 commit comments

Comments
 (0)