From 49f174cdad9db9d7b71b55e14b080e8508f629a8 Mon Sep 17 00:00:00 2001 From: Jordan Walke Date: Fri, 23 Aug 2013 02:04:07 -0700 Subject: [PATCH 1/2] Server rendering: rendering of entire document using React. Summary: Allows rendering of React into the "document" as opposed to into a particular node. To recap some basics: document: One level above the tag - like the browser. document.documentElement: To support full-page server side rendering, we need to be able to render *everything* including the HTML/BODY tags. This allows that. --- src/core/ReactComponentBrowserEnvironment.js | 8 +++++++- src/core/ReactMount.js | 4 ++++ src/vendor/core/createNodesFromMarkup.js | 6 +++++- src/vendor/core/getMarkupWrap.js | 8 +++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/core/ReactComponentBrowserEnvironment.js b/src/core/ReactComponentBrowserEnvironment.js index 26a3e27739..c08630cec5 100644 --- a/src/core/ReactComponentBrowserEnvironment.js +++ b/src/core/ReactComponentBrowserEnvironment.js @@ -29,6 +29,9 @@ var getReactRootElementInContainer = require('getReactRootElementInContainer'); var invariant = require('invariant'); +var ELEMENT_NODE_TYPE = 1; +var DOC_NODE_TYPE = 9; + /** * Abstracts away all functionality of `ReactComponent` requires knowledge of @@ -78,7 +81,10 @@ var ReactComponentBrowserEnvironment = { */ mountImageIntoNode: function(markup, container, shouldReuseMarkup) { invariant( - container && container.nodeType === 1, + container && ( + container.nodeType === ELEMENT_NODE_TYPE || + container.nodeType === DOC_NODE_TYPE && ReactMount.allowFullPageRender + ), 'mountComponentIntoNode(...): Target container is not a DOM element.' ); if (shouldReuseMarkup) { diff --git a/src/core/ReactMount.js b/src/core/ReactMount.js index dc1fd92aa1..9bb4c4efd5 100644 --- a/src/core/ReactMount.js +++ b/src/core/ReactMount.js @@ -197,6 +197,10 @@ function purgeID(id) { * Inside of `container`, the first element rendered is the "reactRoot". */ var ReactMount = { + /** + * Safety guard to prevent accidentally rendering over the entire HTML tree. + */ + allowFullPageRender: false, /** Time spent generating markup. */ totalInstantiationTime: 0, diff --git a/src/vendor/core/createNodesFromMarkup.js b/src/vendor/core/createNodesFromMarkup.js index 6ef538fa40..57b93e6cba 100644 --- a/src/vendor/core/createNodesFromMarkup.js +++ b/src/vendor/core/createNodesFromMarkup.js @@ -19,6 +19,8 @@ /*jslint evil: true, sub: true */ +var ExecutionEnvironment = require('ExecutionEnvironment'); + var createArrayFrom = require('createArrayFrom'); var getMarkupWrap = require('getMarkupWrap'); var invariant = require('invariant'); @@ -26,7 +28,8 @@ var invariant = require('invariant'); /** * Dummy container used to render all markup. */ -var dummyNode = document.createElement('div'); +var dummyNode = + ExecutionEnvironment.canUseDOM ? document.createElement('div') : null; /** * Pattern used by `getNodeName`. @@ -56,6 +59,7 @@ function getNodeName(markup) { */ function createNodesFromMarkup(markup, handleScript) { var node = dummyNode; + invariant(!!dummyNode, 'createNodesFromMarkup dummy not initialized'); var nodeName = getNodeName(markup); var wrap = nodeName && getMarkupWrap(nodeName); diff --git a/src/vendor/core/getMarkupWrap.js b/src/vendor/core/getMarkupWrap.js index c35a9d0158..91dac43e19 100644 --- a/src/vendor/core/getMarkupWrap.js +++ b/src/vendor/core/getMarkupWrap.js @@ -16,10 +16,15 @@ * @providesModule getMarkupWrap */ +var ExecutionEnvironment = require('ExecutionEnvironment'); + +var invariant = require('invariant'); + /** * Dummy container used to detect which wraps are necessary. */ -var dummyNode = document.createElement('div'); +var dummyNode = + ExecutionEnvironment.canUseDOM ? document.createElement('div') : null; /** * Some browsers cannot use `innerHTML` to render certain elements standalone, @@ -55,6 +60,7 @@ var markupWrap = { * @return {?array} Markup wrap configuration, if applicable. */ function getMarkupWrap(nodeName) { + invariant(!!dummyNode, 'Markup wrapping node not initialized'); if (!markupWrap.hasOwnProperty(nodeName)) { nodeName = '*'; } From 748ed6cd818a7b18c5ea020265844d6713533452 Mon Sep 17 00:00:00 2001 From: Jordan Walke Date: Fri, 23 Aug 2013 12:32:35 -0700 Subject: [PATCH 2/2] adding better test - moving execution env module. --- src/core/ReactComponentBrowserEnvironment.js | 2 +- src/core/__tests__/ReactComponent-test.js | 20 +++++++++++++++++-- .../core}/ExecutionEnvironment.js | 0 3 files changed, 19 insertions(+), 3 deletions(-) rename src/{environment => vendor/core}/ExecutionEnvironment.js (100%) diff --git a/src/core/ReactComponentBrowserEnvironment.js b/src/core/ReactComponentBrowserEnvironment.js index c08630cec5..5c60bcb8af 100644 --- a/src/core/ReactComponentBrowserEnvironment.js +++ b/src/core/ReactComponentBrowserEnvironment.js @@ -85,7 +85,7 @@ var ReactComponentBrowserEnvironment = { container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE && ReactMount.allowFullPageRender ), - 'mountComponentIntoNode(...): Target container is not a DOM element.' + 'mountComponentIntoNode(...): Target container is not valid.' ); if (shouldReuseMarkup) { if (ReactMarkupChecksum.canReuseMarkup( diff --git a/src/core/__tests__/ReactComponent-test.js b/src/core/__tests__/ReactComponent-test.js index af242c350d..0a78d56915 100644 --- a/src/core/__tests__/ReactComponent-test.js +++ b/src/core/__tests__/ReactComponent-test.js @@ -20,6 +20,7 @@ "use strict"; var React; +var ReactMount; var ReactTestUtils; var reactComponentExpect; @@ -27,10 +28,25 @@ var reactComponentExpect; describe('ReactComponent', function() { beforeEach(function() { React = require('React'); + ReactMount = require('ReactMount'); ReactTestUtils = require('ReactTestUtils'); reactComponentExpect = require('reactComponentExpect'); }); + it('should not throw on full document rendering', function() { + var container = {nodeType: 9}; + expect(function() { + React.renderComponent(
, container); + }).toThrow( + 'Invariant Violation: mountComponentIntoNode(...): Target container is ' + + 'not valid.' + ); + ReactMount.allowFullPageRender = true; + expect(function() { + React.renderComponent(
, container); + }).not.toThrow(); + }); + it('should throw on invalid render targets', function() { var container = document.createElement('div'); // jQuery objects are basically arrays; people often pass them in by mistake @@ -38,14 +54,14 @@ describe('ReactComponent', function() { React.renderComponent(
, [container]); }).toThrow( 'Invariant Violation: mountComponentIntoNode(...): Target container is ' + - 'not a DOM element.' + 'not valid.' ); expect(function() { React.renderComponent(
, null); }).toThrow( 'Invariant Violation: mountComponentIntoNode(...): Target container is ' + - 'not a DOM element.' + 'not valid.' ); }); diff --git a/src/environment/ExecutionEnvironment.js b/src/vendor/core/ExecutionEnvironment.js similarity index 100% rename from src/environment/ExecutionEnvironment.js rename to src/vendor/core/ExecutionEnvironment.js