Skip to content

Commit

Permalink
chore(examples): make it an isomorphic app
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchentw committed May 1, 2015
1 parent f74ee5a commit 931aaab
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ node_modules
.module-cache
bower_components
public/assets
public/tmp
public/index.html
public/*.gz
145 changes: 145 additions & 0 deletions examples/gh-pages/IsomorphicReactPluginFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use strict";

var React = require("react");
var evaluate = require("eval");

var SEPERATOR = "----------------------------------------";// 40 chars

function IsomorphicReactPluginFactory (_options_) {
var options = _options_ || {};
this.serverComponentPath = options.serverComponentPath;
this.serverMarkupPath = options.serverMarkupPath;
this.htmlOutputPath = options.htmlOutputPath;

if ("string" !== typeof options.htmlDoctype) {
options.htmlDoctype = "<!DOCTYPE html>";
}
this.htmlDoctype = options.htmlDoctype;

var evaluateOptions = this.evaluateOptions = options.evaluateOptions || {};
if (false !== evaluateOptions.includeGlobals) {
evaluateOptions.includeGlobals = true;
}

return {
clientPlugin: new ClientPlugin(this),
serverPlugin: new ServerPlugin(this),
};
}

IsomorphicReactPluginFactory.prototype.receivedClientAssets = function (clientAssets) {
this.clientAssets = clientAssets;
if (this.serverAssets) {
this.runCompilation();
}
}

IsomorphicReactPluginFactory.prototype.receivedServerEmit = function (serverAssets, done) {
this.serverAssets = serverAssets;
this.done = done;
if (this.clientAssets) {
this.runCompilation();
}
}

IsomorphicReactPluginFactory.prototype.evaluate = function (serverPath, errorMessage) {
var serverAsset = this.serverAssets[serverPath];
if (undefined === serverAsset) {
throw new Error(errorMessage + ": \"" + serverPath + "\"");
}

var source = serverAsset.source();
var includeGlobals = this.evaluateOptions.includeGlobals;

try {
return evaluate(source, includeGlobals);
} catch (error) {
error.message = serverPath + "\n" + SEPERATOR + SEPERATOR + error.message;
error.stack = error.stack + "\n" + SEPERATOR + SEPERATOR + source + "\n" + SEPERATOR + SEPERATOR;
throw error;
}
}

IsomorphicReactPluginFactory.prototype.runCompilation = function () {
var error;

try {
var ServerComponent = this.evaluate(this.serverComponentPath, "Component for server rendering is missing");
var componentString = React.renderToString(React.createElement(ServerComponent));

var MarkupComponent = this.evaluate(this.serverMarkupPath, "Component for markup generation is missing");
var htmlString = React.renderToStaticMarkup(
React.createElement(MarkupComponent, {
componentString: componentString,
clientAssets: this.clientAssets,
})
);

this.serverAssets[this.htmlOutputPath] = createAssetFromContents(
this.htmlDoctype + htmlString
);

} catch (_error_) {
error = _error_;
}
this.done(error);
}

function ClientPlugin (isomorphicReactPluginFactory) {
this.pluginFactory = isomorphicReactPluginFactory;
}

ClientPlugin.prototype.apply = function (compiler) {
compiler.plugin("emit", (function (compilation, done) {
var assets = getAssetsFromCompilation(compilation);

this.pluginFactory.receivedClientAssets(assets);

done();
}).bind(this));
}

function ServerPlugin (isomorphicReactPluginFactory) {
this.pluginFactory = isomorphicReactPluginFactory;
}

ServerPlugin.prototype.apply = function (compiler) {
compiler.plugin("emit", (function (compilation, done) {

this.pluginFactory.receivedServerEmit(compilation.assets, done);
}).bind(this));
}

module.exports = IsomorphicReactPluginFactory;

// Shamelessly stolen from html-webpack-plugin - Thanks @ampedandwired :)
function getAssetsFromCompilation (compiler) {
var assets = {};
var webpackStatsJson = compiler.getStats().toJson();
for (var chunk in webpackStatsJson.assetsByChunkName) {
var chunkValue = webpackStatsJson.assetsByChunkName[chunk];

// Webpack outputs an array for each chunk when using sourcemaps
if (chunkValue instanceof Array) {
// Is the main bundle always the first element?
chunkValue = chunkValue[0];
}

if (compiler.options.output.publicPath) {
chunkValue = compiler.options.output.publicPath + chunkValue;
}
assets[chunk] = chunkValue;
}
return assets;
}

function createAssetFromContents(contents) {
return {
source: function() {
return contents;
},
size: function() {
return contents.length;
}
};
}
10 changes: 0 additions & 10 deletions examples/gh-pages/index.html

This file was deleted.

8 changes: 5 additions & 3 deletions examples/gh-pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
"name": "gh-pages",
"version": "1.0.0",
"scripts": {
"build_client": "webpack -p",
"dev": "webpack && webpack-dev-server --inline --hot --content-base ../../public"
"clean": "rm -f ../../public/index.html && rm -rf ../../public/assets && rm -rf ../../public/tmp",
"build_client": "npm run clean && webpack -p",
"dev": "npm run clean && webpack-dev-server --content-base ../../public"
},
"devDependencies": {
"devDependencies": {
"babel-core": "^5.1.13",
"babel-loader": "^5.0.0",
"bootstrap-sass": "^3.3.4",
"classnames": "^1.2.1",
"css-loader": "^0.12.0",
"eval": "^0.1.0",
"file-loader": "^0.8.1",
"github-fork-ribbon-css": "git+https://github.com/tomchentw/github-fork-ribbon-css.git#gh-pages",
"html-webpack-plugin": "^1.1.0",
Expand Down
8 changes: 6 additions & 2 deletions examples/gh-pages/scripts/ReactRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,12 @@ const ALL_ACTIONS = ACTIONS.concat(DROPDOWN_ACTIONS.filter((x) => { return !!x;
const ReactRoot = React.createClass({

getInitialState () {
var hash = location.hash || ACTIONS[0].path,
action = ALL_ACTIONS.filter((action) => { return action.path === hash; })[0];
const location = (
"undefined" !== typeof window && location || {
}
);
const hash = location.hash || ACTIONS[0].path;
const action = ALL_ACTIONS.filter((action) => { return action.path === hash; })[0];

return {
action: action,
Expand Down
6 changes: 4 additions & 2 deletions examples/gh-pages/scripts/components/GettingStarted.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const GettingStarted = React.createClass({
ref="map"
googleMapsApi={googleMapsApi}
zoom={3}
center={new google.maps.LatLng(-25.363882, 131.044922)}
center={{lat: -25.363882, lng: 131.044922}}
onClick={this._handle_map_click}>
{state.markers.map(toMarker, this)}
</GoogleMaps>
Expand All @@ -88,7 +88,9 @@ const GettingStarted = React.createClass({
export default React.createClass({
render () {
return (
<GettingStarted googleMapsApi={google.maps} {...this.props} />
<GettingStarted googleMapsApi={
"undefined" !== typeof google ? google.maps : null
} {...this.props} />
);
}
});
13 changes: 7 additions & 6 deletions examples/gh-pages/scripts/components/basics/Geolocation.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from "react/addons";
import {GoogleMaps, Circle, InfoWindow} from "react-google-maps";

var {geolocation} = navigator;
const geolocation = (
"undefined" !== typeof window && navigator && navigator.geolocation || {
getCurrentPosition: (success, failure) => {
failure("Your browser doesn't support geolocation.");
},
}
);

if (!geolocation) {
geolocation = {
getCurrentPosition: (success, failure) => { failure("Your browser doesn't support geolocation."); }
};
}
/*
* https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
*/
Expand Down
32 changes: 32 additions & 0 deletions examples/gh-pages/scripts/html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react/addons";

const {PropTypes} = React;

const ReactHtml = React.createClass({

propTypes: {
componentString: PropTypes.string.isRequired,
clientAssets: PropTypes.object.isRequired,
},

render () {
const {props, state} = this,
{clientAssets} = props;

return (
<html>
<head>
<title>React Google Maps | tomchentw</title>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp" />
<script type="text/javascript" src="prism.min.js" />
</head>
<body>
<div id="react-container" dangerouslySetInnerHTML={{__html: props.componentString}}/>
<script type="text/javascript" src={clientAssets["assets/main"]} />
</body>
</html>
);
}
});

export default ReactHtml;
5 changes: 5 additions & 0 deletions examples/gh-pages/scripts/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from "react/addons";

import ReactRoot from "./ReactRoot";

export default ReactRoot;
Loading

0 comments on commit 931aaab

Please sign in to comment.