├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── dist ├── react-server.dev.js ├── react-server.js ├── react-server.min.js └── react.js ├── index.js ├── lib ├── React.js ├── ReactClass.js ├── ReactComponent.js ├── ReactComponentFactory.js ├── ReactCompositeComponent.js ├── ReactDomComponent.js ├── ReactTextComponent.js └── addons.js ├── package.json ├── register.js ├── tests ├── bench.js ├── fixtures │ ├── apps │ │ ├── chat │ │ │ ├── app.js │ │ │ ├── components │ │ │ │ ├── ChatApp.jsx │ │ │ │ ├── Html.jsx │ │ │ │ ├── MessageComposer.jsx │ │ │ │ ├── MessageListItem.jsx │ │ │ │ ├── MessageSection.jsx │ │ │ │ ├── ThreadListItem.jsx │ │ │ │ └── ThreadSection.jsx │ │ │ ├── configs │ │ │ │ └── routes.js │ │ │ ├── package.json │ │ │ ├── state.json │ │ │ └── stores │ │ │ │ ├── MessageStore.js │ │ │ │ ├── RouteStore.js │ │ │ │ ├── ThreadStore.js │ │ │ │ └── UnreadThreadStore.js │ │ ├── fluxible-router │ │ │ ├── app.js │ │ │ ├── components │ │ │ │ ├── About.js │ │ │ │ ├── Application.js │ │ │ │ ├── Home.js │ │ │ │ ├── Html.js │ │ │ │ ├── Nav.js │ │ │ │ ├── Page.js │ │ │ │ └── Timestamp.js │ │ │ ├── configs │ │ │ │ └── routes.js │ │ │ ├── package.json │ │ │ ├── state.json │ │ │ └── stores │ │ │ │ ├── ApplicationStore.js │ │ │ │ ├── PageStore.js │ │ │ │ ├── RouteStore.js │ │ │ │ └── TimeStore.js │ │ └── todo │ │ │ ├── app.js │ │ │ ├── components │ │ │ ├── Footer.jsx │ │ │ ├── Html.jsx │ │ │ ├── TodoApp.jsx │ │ │ └── TodoItem.jsx │ │ │ ├── package.json │ │ │ ├── state.json │ │ │ └── stores │ │ │ ├── PageStore.js │ │ │ └── TodoStore.js │ └── components │ │ ├── ContextChild.js │ │ ├── ContextParent.js │ │ ├── Hello.js │ │ └── HelloCreateClass.js ├── profile │ ├── react-server.js │ └── react.js └── unit │ ├── .eslintrc │ ├── apps.js │ ├── context.js │ ├── hello.js │ ├── native.js │ └── stateful.js ├── utils └── renderAppWithFreshReact.js └── webpack.config.js /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | parser: "babel-eslint" 3 | env: 4 | node: true 5 | rules: 6 | strict: 0 7 | indent: [2, 4] 8 | quotes: [0] 9 | no-unused-vars: 0 // see https://github.com/babel/babel-eslint/issues/21 10 | no-underscore-dangle: 0 11 | no-use-before-define: 0 12 | no-unused-expressions: 0 13 | yoda: 0 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | artifacts/ 3 | **/npm-debug.log 4 | **/ynpm-debug.log 5 | .DS_Store 6 | *~ 7 | test/ 8 | tests/ 9 | docs/ 10 | examples/ 11 | screwdriver/ 12 | Makefile 13 | dist/react.js 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | matrix: 4 | allow_failures: 5 | - node_js: "0.12" 6 | node_js: 7 | - "0.10" 8 | - "0.12" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For React software 4 | 5 | Copyright (c) 2013-2015, Facebook, Inc. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | * Neither the name Facebook nor the names of its contributors may be used to 19 | endorse or promote products derived from this software without specific 20 | prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-server 2 | 3 | This project seeks to re-implement React's `renderToString` and 4 | `renderToStaticMarkup` methods to be more efficient for server rendering. The 5 | resulting markup is intended to be equivalent to that of stock React so that 6 | the client (which will use stock React) will be able to re-use the server 7 | rendered markup. 8 | 9 | ## Install 10 | 11 | Not available on npm. You must install from git. 12 | 13 | ## Usage 14 | 15 | react-server is meant to be a drop in replacement for React specifically for the 16 | server. It should not be used on the client. 17 | 18 | You can enable react-server by doing the following: 19 | 20 | ```js 21 | require('react-server/register'); 22 | ``` 23 | 24 | Internally this is overriding node's require cache to return react-server 25 | whenever `require('react')` or `require('react/addons')` is called. 26 | 27 | ## Behavioral Differences 28 | 29 | In order to gain these efficiencies, some of the inner workings of React have 30 | been changed which may bleed in to how components are implemented. 31 | 32 | ### Autobinding 33 | 34 | `React.createClass` automatically binds all non-static methods to the component 35 | instance so that you do not need to automatically bind them. This is typically 36 | used when registering event handlers or passing methods between components. If 37 | your component relies on this, it's not as simple as just using bind because 38 | React on the client will warn about binding methods that are already autobound. 39 | Instead, you can create a closure: 40 | 41 | ``` 42 | React.createClass({ 43 | render: function () { 44 | var self = this; 45 | return foo 48 | } 49 | }); 50 | ``` 51 | 52 | ### Owner vs. Parent Context 53 | 54 | react-server uses parent context which is slightly different to React 0.13's 55 | owner context. React plans on switching to parent context in version 0.14, but 56 | react-server chose parent context due to its simpler implementation. In most 57 | cases, this is actually more reliable than the old owner context. 58 | 59 | ## Missing Utilities 60 | 61 | react-server may still be missing functionality from stock React. If you find 62 | any missing pieces or broken functionality, please open an issue to let me know. 63 | 64 | ## Testing 65 | 66 | You can run the test suite and benchmarks simply by running: 67 | 68 | ``` 69 | npm test 70 | ``` 71 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var assign = require('object-assign'); 2 | 3 | // Monkey patch setState to be synchronous and not deal with update queue 4 | var ReactComponent = require('react/lib/ReactComponent'); 5 | ReactComponent.prototype.setState = function (state) { 6 | this.state = assign({}, this.state, state); 7 | }; 8 | 9 | var ReactServer = require('./lib/React'); 10 | 11 | // Ensure that all addons use ./lib/React as React dependency 12 | require.cache[require.resolve('react/lib/React')] = require.cache[require.resolve('./lib/React')]; 13 | 14 | ReactServer.addons = require('./lib/addons'); 15 | 16 | module.exports = ReactServer; 17 | -------------------------------------------------------------------------------- /lib/React.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2015, Facebook, Inc. 3 | * All rights reserved. 4 | */ 5 | 6 | var assign = require('object-assign'); 7 | 8 | import ReactServerClass from './ReactClass'; 9 | import ReactClass from 'react/lib/ReactClass'; 10 | 11 | 12 | // Prevent autobind on built-in components 13 | ReactClass.createClass = ReactServerClass.createClass; 14 | 15 | import ReactDomComponent from './ReactDomComponent'; 16 | import { 17 | instantiateReactComponent, 18 | injections as ReactComponentFactoryInjections 19 | } from './ReactComponentFactory.js'; 20 | import ReactCompositeComponent from './ReactCompositeComponent'; 21 | 22 | import ReactChildren from 'react/lib/ReactChildren'; 23 | import ReactComponent from 'react/lib/ReactComponent'; 24 | import ReactDefaultInjection from 'react/lib/ReactDefaultInjection'; 25 | import ReactDOM from 'react/lib/ReactDOM'; 26 | import ReactElement from 'react/lib/ReactElement'; 27 | import ReactInstanceHandles from 'react/lib/ReactInstanceHandles'; 28 | import ReactMarkupChecksum from 'react/lib/ReactMarkupChecksum'; 29 | import ReactNativeComponent from 'react/lib/ReactNativeComponent'; 30 | 31 | ReactDefaultInjection.inject(); 32 | ReactComponentFactoryInjections.ReactCompositeComponent = ReactCompositeComponent; 33 | ReactComponentFactoryInjections.ReactDomComponent = ReactDomComponent; 34 | 35 | var ReactServer = { 36 | Children: { 37 | map: ReactChildren.map, 38 | forEach: ReactChildren.forEach, 39 | count: ReactChildren.count, 40 | only: require('react/lib/onlyChild') 41 | }, 42 | Component: ReactComponent, 43 | createClass: ReactServerClass.createClass, 44 | createElement: ReactElement.createElement, 45 | cloneElement: ReactElement.cloneElement, 46 | createFactory: ReactElement.createFactory, 47 | DOM: ReactDOM, 48 | isValidElement: ReactElement.isValidElement, 49 | PropTypes: require('react/lib/ReactPropTypes'), 50 | renderToStaticMarkup: function (element, callback) { 51 | var component = instantiateReactComponent(element); 52 | var markup = component.mountComponent(null, { 53 | renderToStaticMarkup: true 54 | }, {}); 55 | callback && callback(null, markup); 56 | return markup; 57 | }, 58 | renderToString: function (element, callback) { 59 | var rootNodeId = ReactInstanceHandles.createReactRootID(); 60 | var component = instantiateReactComponent(element); 61 | var markup = component.mountComponent(rootNodeId, { 62 | renderToStaticMarkup: false 63 | }, {}); 64 | markup = ReactMarkupChecksum.addChecksumToMarkup(markup); 65 | callback && callback(null, markup); 66 | return markup; 67 | }, 68 | version: '0.13.3', 69 | 70 | __spread: assign, 71 | 72 | // Allows seeing if react-server is used 73 | __isReactServer: true 74 | }; 75 | 76 | module.exports = ReactServer; 77 | -------------------------------------------------------------------------------- /lib/ReactClass.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule ReactClass 10 | */ 11 | 12 | 'use strict'; 13 | 14 | var ReactComponent = require("react/lib/ReactComponent"); 15 | var ReactElement = require("react/lib/ReactElement"); 16 | var ReactErrorUtils = require("react/lib/ReactErrorUtils"); 17 | var ReactInstanceMap = require("react/lib/ReactInstanceMap"); 18 | var ReactLifeCycle = require("react/lib/ReactLifeCycle"); 19 | var ReactPropTypeLocations = require("react/lib/ReactPropTypeLocations"); 20 | var ReactPropTypeLocationNames = require("react/lib/ReactPropTypeLocationNames"); 21 | var ReactUpdateQueue = require("react/lib/ReactUpdateQueue"); 22 | 23 | var assign = require("object-assign"); 24 | var invariant = require("react/lib/invariant"); 25 | var keyMirror = require("react/lib/keyMirror"); 26 | var keyOf = require("react/lib/keyOf"); 27 | var warning = require("react/lib/warning"); 28 | 29 | var MIXINS_KEY = keyOf({mixins: null}); 30 | 31 | /** 32 | * Policies that describe methods in `ReactClassInterface`. 33 | */ 34 | var SpecPolicy = keyMirror({ 35 | /** 36 | * These methods may be defined only once by the class specification or mixin. 37 | */ 38 | DEFINE_ONCE: null, 39 | /** 40 | * These methods may be defined by both the class specification and mixins. 41 | * Subsequent definitions will be chained. These methods must return void. 42 | */ 43 | DEFINE_MANY: null, 44 | /** 45 | * These methods are overriding the base class. 46 | */ 47 | OVERRIDE_BASE: null, 48 | /** 49 | * These methods are similar to DEFINE_MANY, except we assume they return 50 | * objects. We try to merge the keys of the return values of all the mixed in 51 | * functions. If there is a key conflict we throw. 52 | */ 53 | DEFINE_MANY_MERGED: null 54 | }); 55 | 56 | 57 | var injectedMixins = []; 58 | 59 | /** 60 | * Composite components are higher-level components that compose other composite 61 | * or native components. 62 | * 63 | * To create a new type of `ReactClass`, pass a specification of 64 | * your new class to `React.createClass`. The only requirement of your class 65 | * specification is that you implement a `render` method. 66 | * 67 | * var MyComponent = React.createClass({ 68 | * render: function() { 69 | * return
, or