diff --git a/fixtures/dom/.gitignore b/fixtures/dom/.gitignore index bcc245d36a..bffad9e24b 100644 --- a/fixtures/dom/.gitignore +++ b/fixtures/dom/.gitignore @@ -10,6 +10,7 @@ coverage build public/react.development.js public/react-dom.development.js +public/react-dom-server.browser.development.js # misc .DS_Store diff --git a/fixtures/dom/package.json b/fixtures/dom/package.json index ef92dbfe50..0f2959684a 100644 --- a/fixtures/dom/package.json +++ b/fixtures/dom/package.json @@ -6,7 +6,9 @@ "react-scripts": "^1.0.11" }, "dependencies": { + "@babel/standalone": "^7.0.0", "classnames": "^2.2.5", + "codemirror": "^5.40.0", "core-js": "^2.4.1", "prop-types": "^15.6.0", "query-string": "^4.2.3", @@ -16,7 +18,7 @@ }, "scripts": { "start": "react-scripts start", - "prestart": "cp ../../build/dist/react.development.js public/ && cp ../../build/dist/react-dom.development.js public/", + "prestart": "cp ../../build/dist/react.development.js ../../build/dist/react-dom.development.js ../../build/dist/react-dom-server.browser.development.js public/", "build": "react-scripts build && cp build/index.html build/200.html", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" diff --git a/fixtures/dom/public/renderer.html b/fixtures/dom/public/renderer.html new file mode 100644 index 0000000000..3852dd952f --- /dev/null +++ b/fixtures/dom/public/renderer.html @@ -0,0 +1,86 @@ + + + + + + Renderer + + + +
+

Loading

+ + + + + +
+ +
+ + + + + diff --git a/fixtures/dom/public/renderer.js b/fixtures/dom/public/renderer.js new file mode 100644 index 0000000000..542580a073 --- /dev/null +++ b/fixtures/dom/public/renderer.js @@ -0,0 +1,141 @@ +/** + * Supports render.html, a piece of the hydration fixture. See /hydration + */ + +'use strict'; + +(function() { + var Fixture = null; + var output = document.getElementById('output'); + var status = document.getElementById('status'); + var hydrate = document.getElementById('hydrate'); + var reload = document.getElementById('reload'); + var renders = 0; + var failed = false; + + function getQueryParam(key) { + var pattern = new RegExp(key + '=([^&]+)(&|$)'); + var matches = window.location.search.match(pattern); + + if (matches) { + return decodeURIComponent(matches[1]); + } + + handleError(new Error('No key found for' + key)); + } + + function getBooleanQueryParam(key) { + return getQueryParam(key) === 'true'; + } + + function setStatus(label) { + status.innerHTML = label; + } + + function prerender() { + setStatus('Generating markup'); + + output.innerHTML = ReactDOMServer.renderToString( + React.createElement(Fixture) + ); + + setStatus('Markup only (No React)'); + } + + function render() { + setStatus('Hydrating'); + + if (ReactDOM.hydrate) { + ReactDOM.hydrate(React.createElement(Fixture), output); + } else { + ReactDOM.render(React.createElement(Fixture), output); + } + + setStatus(renders > 0 ? 'Re-rendered (' + renders + 'x)' : 'Hydrated'); + renders += 1; + hydrate.innerHTML = 'Re-render'; + } + + function handleError(error) { + console.log(error); + failed = true; + setStatus('Javascript Error'); + output.innerHTML = error; + } + + function loadScript(src) { + return new Promise(function(resolve, reject) { + var script = document.createElement('script'); + script.async = true; + script.src = src; + + script.onload = resolve; + script.onerror = function(error) { + reject(new Error('Unable to load ' + src)); + }; + + document.body.appendChild(script); + }); + } + + function injectFixture(src) { + Fixture = new Function(src + '\nreturn Fixture;')(); + + if (typeof Fixture === 'undefined') { + setStatus('Failed'); + output.innerHTML = 'Please name your root component "Fixture"'; + } else { + prerender(); + + if (getBooleanQueryParam('hydrate')) { + render(); + } + } + } + + function reloadFixture(code) { + renders = 0; + ReactDOM.unmountComponentAtNode(output); + injectFixture(code); + } + + window.onerror = handleError; + + reload.onclick = function() { + window.location.reload(); + }; + + hydrate.onclick = render; + + loadScript(getQueryParam('reactPath')) + .then(function() { + return getBooleanQueryParam('needsReactDOM') + ? loadScript(getQueryParam('reactDOMPath')) + : null; + }) + .then(function() { + return loadScript(getQueryParam('reactDOMServerPath')); + }) + .then(function() { + if (failed) { + return; + } + + window.addEventListener('message', function(event) { + var data = JSON.parse(event.data); + + switch (data.type) { + case 'code': + reloadFixture(data.payload); + break; + default: + throw new Error( + 'Renderer Error: Unrecognized message "' + data.type + '"' + ); + } + }); + + window.parent.postMessage(JSON.stringify({type: 'ready'}), '*'); + }) + .catch(handleError); +})(); diff --git a/fixtures/dom/src/components/App.js b/fixtures/dom/src/components/App.js index 084283733f..5b0de92e07 100644 --- a/fixtures/dom/src/components/App.js +++ b/fixtures/dom/src/components/App.js @@ -8,9 +8,7 @@ function App() { return (
-
- -
+
); } diff --git a/fixtures/dom/src/components/FixtureSet.js b/fixtures/dom/src/components/FixtureSet.js index ee42704368..589b1c7bc3 100644 --- a/fixtures/dom/src/components/FixtureSet.js +++ b/fixtures/dom/src/components/FixtureSet.js @@ -11,7 +11,7 @@ class FixtureSet extends React.Component { const {title, description, children} = this.props; return ( -
+

{title}

{description &&

{description}

} diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index de71a7dca6..9671d2c1d9 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -48,7 +48,8 @@ class Header extends React.Component {