From 89bdff92990262889696d9cb461fc5dd2d4dee24 Mon Sep 17 00:00:00 2001 From: Wing Leung Date: Thu, 10 May 2018 18:54:04 +0200 Subject: [PATCH 1/3] split client and server logic --- client.js | 1 + server.js | 1 + src/client.js | 32 ++++++++++++ src/server.js | 22 +++++++++ test/renderReact-test.js | 90 +++++++++++++++++++++++++++++++++- test/renderReactStatic-test.js | 35 +++++++++++++ 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 client.js create mode 100644 server.js create mode 100644 src/client.js create mode 100644 src/server.js diff --git a/client.js b/client.js new file mode 100644 index 0000000..4fba4f1 --- /dev/null +++ b/client.js @@ -0,0 +1 @@ +module.exports = require('./lib/client.js'); diff --git a/server.js b/server.js new file mode 100644 index 0000000..429f220 --- /dev/null +++ b/server.js @@ -0,0 +1 @@ +module.exports = require('./lib/server.js'); diff --git a/src/client.js b/src/client.js new file mode 100644 index 0000000..7989367 --- /dev/null +++ b/src/client.js @@ -0,0 +1,32 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import hypernova, { load } from 'hypernova'; + +export const renderReact = (name, component) => hypernova({ + server() {}, + + client() { + const payloads = load(name); + + if (payloads) { + payloads.forEach((payload) => { + const { node, data } = payload; + const element = React.createElement(component, data); + + if (ReactDOM.hydrate) { + ReactDOM.hydrate(element, node); + } else { + ReactDOM.render(element, node); + } + }); + } + + return component; + }, +}); + +export const renderReactStatic = () => hypernova({ + server() {}, + + client() {}, +}); diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..bc10a84 --- /dev/null +++ b/src/server.js @@ -0,0 +1,22 @@ +import React from 'react'; +import ReactDOMServer from 'react-dom/server'; +import hypernova, { serialize } from 'hypernova'; + +export const renderReact = (name, component) => hypernova({ + server() { + return (props) => { + const contents = ReactDOMServer.renderToString(React.createElement(component, props)); + return serialize(name, contents, props); + }; + }, + + client() { }, +}); + +export const renderReactStatic = (name, component) => hypernova({ + server() { + return props => ReactDOMServer.renderToStaticMarkup(React.createElement(component, props)); + }, + + client() {}, +}); diff --git a/test/renderReact-test.js b/test/renderReact-test.js index e6a97f5..af231e4 100644 --- a/test/renderReact-test.js +++ b/test/renderReact-test.js @@ -6,6 +6,8 @@ import ifReact from 'enzyme-adapter-react-helper/build/ifReact'; import ExampleReactComponent from './components/ExampleReactComponent'; import { renderReact } from '..'; +import { renderReact as renderReactClient } from '../lib/client'; +import { renderReact as renderReactServer } from '../lib/server'; describe('renderReact', () => { let result; @@ -40,11 +42,81 @@ describe('renderReact', () => { assert(hydrateMethod.calledOnce); + hydrateMethod.restore(); + delete global.window; delete global.document; + done(); + }); + }); + + it('calls hypernova.client (render method)', (done) => { + jsdom.env(result, (err, window) => { + if (err) { + done(err); + return; + } + + const sandbox = sinon.createSandbox(); + if (ReactDOM.hydrate) { + sandbox.stub(ReactDOM, 'hydrate').value(undefined); + } + + const renderMethod = sinon.spy(ReactDOM, 'render'); + + global.window = window; + global.document = window.document; + + // Calling it again for the client. + renderReact('ExampleReactComponent', ExampleReactComponent); + + assert(renderMethod.calledOnce); + + sandbox.restore(); + renderMethod.restore(); + + delete global.window; + delete global.document; + + done(); + }); + }); +}); + +describe('renderReact client side endpoint', () => { + let result; + beforeEach(() => { + result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); + }); + + it('exists', () => { + assert.isFunction(renderReactClient); + assert.equal(renderReactClient.length, 2); + }); + + ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => { + jsdom.env(result, (err, window) => { + if (err) { + done(err); + return; + } + + global.window = window; + global.document = window.document; + + const hydrateMethod = sinon.spy(ReactDOM, 'hydrate'); + + // Calling it again for the client. + renderReactClient('ExampleReactComponent', ExampleReactComponent); + + assert(hydrateMethod.calledOnce); + hydrateMethod.restore(); + delete global.window; + delete global.document; + done(); }); }); @@ -67,12 +139,14 @@ describe('renderReact', () => { global.document = window.document; // Calling it again for the client. - renderReact('ExampleReactComponent', ExampleReactComponent); + renderReactClient('ExampleReactComponent', ExampleReactComponent); assert(renderMethod.calledOnce); sandbox.restore(); + renderMethod.restore(); + delete global.window; delete global.document; @@ -80,3 +154,17 @@ describe('renderReact', () => { }); }); }); + +describe('renderReact server side endpoint', () => { + it('exists', () => { + assert.isFunction(renderReactServer); + assert.equal(renderReactServer.length, 2); + }); + + it('has correct markup on server', () => { + const result = renderReactServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); + + assert.isString(result); + assert.match(result, /Hello Desmond/); + }); +}); diff --git a/test/renderReactStatic-test.js b/test/renderReactStatic-test.js index cdded8c..ccf0819 100644 --- a/test/renderReactStatic-test.js +++ b/test/renderReactStatic-test.js @@ -2,6 +2,7 @@ import { assert } from 'chai'; import ExampleReactComponent from './components/ExampleReactComponent'; import { renderReactStatic } from '..'; +import { renderReactStatic as renderReactStaticServer } from '../lib/server'; describe('renderReactStatic', () => { let result; @@ -19,3 +20,37 @@ describe('renderReactStatic', () => { assert.match(result, /Hello Zack/); }); }); + +describe('renderReactStatic server side endpoint', () => { + let result; + beforeEach(() => { + result = renderReactStaticServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); + }); + + it('exists', () => { + assert.isFunction(renderReactStaticServer); + assert.equal(renderReactStaticServer.length, 2); + }); + + it('has correct markup on server', () => { + assert.isString(result); + assert.match(result, /Hello Zack/); + }); +}); + +describe('renderReactStatic server side endpoint', () => { + let result; + beforeEach(() => { + result = renderReactStaticServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); + }); + + it('exists', () => { + assert.isFunction(renderReactStaticServer); + assert.equal(renderReactStaticServer.length, 2); + }); + + it('has correct markup on server', () => { + assert.isString(result); + assert.match(result, /Hello Zack/); + }); +}); From a0eb6b2e2a57ace4081f429303acb11c68030f61 Mon Sep 17 00:00:00 2001 From: Wing Leung Date: Fri, 11 May 2018 16:14:51 +0200 Subject: [PATCH 2/3] concept approved making further improvements --- src/client.js | 47 +++++---- src/index.js | 31 ++---- src/server.js | 28 ++++-- test/renderReact-test.js | 143 +++------------------------ test/renderReactClient-test.js | 87 ++++++++++++++++ test/renderReactServer-test.js | 26 +++++ test/renderReactStatic-test.js | 47 ++------- test/renderReactStaticServer-test.js | 18 ++++ 8 files changed, 206 insertions(+), 221 deletions(-) create mode 100644 test/renderReactClient-test.js create mode 100644 test/renderReactServer-test.js create mode 100644 test/renderReactStaticServer-test.js diff --git a/src/client.js b/src/client.js index 7989367..d8c24a5 100644 --- a/src/client.js +++ b/src/client.js @@ -2,31 +2,42 @@ import React from 'react'; import ReactDOM from 'react-dom'; import hypernova, { load } from 'hypernova'; -export const renderReact = (name, component) => hypernova({ +const renderReactClient = (name, component) => { + const payloads = load(name); + + if (payloads) { + payloads.forEach((payload) => { + const { node, data } = payload; + const element = React.createElement(component, data); + + if (ReactDOM.hydrate) { + ReactDOM.hydrate(element, node); + } else { + ReactDOM.render(element, node); + } + }); + } + + return component; +}; + +const renderReact = (name, component) => hypernova({ server() {}, client() { - const payloads = load(name); - - if (payloads) { - payloads.forEach((payload) => { - const { node, data } = payload; - const element = React.createElement(component, data); - - if (ReactDOM.hydrate) { - ReactDOM.hydrate(element, node); - } else { - ReactDOM.render(element, node); - } - }); - } - - return component; + return renderReactClient(name, component); }, }); -export const renderReactStatic = () => hypernova({ +/* istanbul ignore next */ +const renderReactStatic = () => hypernova({ server() {}, client() {}, }); + +export { + renderReactClient, + renderReact, + renderReactStatic, +}; diff --git a/src/index.js b/src/index.js index ae8f72a..461c2a4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,39 +1,20 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import ReactDOMServer from 'react-dom/server'; -import hypernova, { serialize, load } from 'hypernova'; +import hypernova from 'hypernova'; +import { renderReactClient } from './client'; +import { renderReactServer, renderReactStaticServer } from './server'; export const renderReact = (name, component) => hypernova({ server() { - return (props) => { - const contents = ReactDOMServer.renderToString(React.createElement(component, props)); - return serialize(name, contents, props); - }; + return renderReactServer(name, component); }, client() { - const payloads = load(name); - - if (payloads) { - payloads.forEach((payload) => { - const { node, data } = payload; - const element = React.createElement(component, data); - - if (ReactDOM.hydrate) { - ReactDOM.hydrate(element, node); - } else { - ReactDOM.render(element, node); - } - }); - } - - return component; + return renderReactClient(name, component); }, }); export const renderReactStatic = (name, component) => hypernova({ server() { - return props => ReactDOMServer.renderToStaticMarkup(React.createElement(component, props)); + return renderReactStaticServer(name, component); }, client() {}, diff --git a/src/server.js b/src/server.js index bc10a84..fcb474e 100644 --- a/src/server.js +++ b/src/server.js @@ -2,21 +2,33 @@ import React from 'react'; import ReactDOMServer from 'react-dom/server'; import hypernova, { serialize } from 'hypernova'; -export const renderReact = (name, component) => hypernova({ +const renderReactServer = (name, component) => (props) => { + const contents = ReactDOMServer.renderToString(React.createElement(component, props)); + return serialize(name, contents, props); +}; + +const renderReactStaticServer = (name, component) => props => + ReactDOMServer.renderToStaticMarkup(React.createElement(component, props)); + +const renderReact = (name, component) => hypernova({ server() { - return (props) => { - const contents = ReactDOMServer.renderToString(React.createElement(component, props)); - return serialize(name, contents, props); - }; + return renderReactServer(name, component); }, - client() { }, + client() {}, }); -export const renderReactStatic = (name, component) => hypernova({ +const renderReactStatic = (name, component) => hypernova({ server() { - return props => ReactDOMServer.renderToStaticMarkup(React.createElement(component, props)); + return renderReactStaticServer(name, component); }, client() {}, }); + +export { + renderReactServer, + renderReactStaticServer, + renderReact, + renderReactStatic, +}; diff --git a/test/renderReact-test.js b/test/renderReact-test.js index af231e4..982984d 100644 --- a/test/renderReact-test.js +++ b/test/renderReact-test.js @@ -1,151 +1,48 @@ import jsdom from 'jsdom'; import { assert } from 'chai'; import sinon from 'sinon'; -import ReactDOM from 'react-dom'; -import ifReact from 'enzyme-adapter-react-helper/build/ifReact'; import ExampleReactComponent from './components/ExampleReactComponent'; import { renderReact } from '..'; -import { renderReact as renderReactClient } from '../lib/client'; -import { renderReact as renderReactServer } from '../lib/server'; +import * as renderReactClientModule from '../lib/client'; +import * as renderReactServerModule from '../lib/server'; describe('renderReact', () => { - let result; - beforeEach(() => { - result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - }); - it('exists', () => { assert.isFunction(renderReact); assert.equal(renderReact.length, 2); }); - it('has correct markup on server', () => { - assert.isString(result); - assert.match(result, /Hello Desmond/); - }); - - ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => { - jsdom.env(result, (err, window) => { - if (err) { - done(err); - return; - } - - global.window = window; - global.document = window.document; - - const hydrateMethod = sinon.spy(ReactDOM, 'hydrate'); - - // Calling it again for the client. - renderReact('ExampleReactComponent', ExampleReactComponent); - - assert(hydrateMethod.calledOnce); - - hydrateMethod.restore(); - - delete global.window; - delete global.document; - - done(); - }); - }); - - it('calls hypernova.client (render method)', (done) => { - jsdom.env(result, (err, window) => { - if (err) { - done(err); - return; - } - - const sandbox = sinon.createSandbox(); - if (ReactDOM.hydrate) { - sandbox.stub(ReactDOM, 'hydrate').value(undefined); - } - - const renderMethod = sinon.spy(ReactDOM, 'render'); - - global.window = window; - global.document = window.document; - - // Calling it again for the client. - renderReact('ExampleReactComponent', ExampleReactComponent); - - assert(renderMethod.calledOnce); + it('calls renderReactServer', () => { + const renderReactServerSpy = sinon.spy(renderReactServerModule, 'renderReactServer'); - sandbox.restore(); - renderMethod.restore(); + renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - delete global.window; - delete global.document; + assert(renderReactServerSpy.calledOnce, `renderReactServer was not called once but ${renderReactServerSpy.callCount} times`); - done(); - }); + renderReactServerSpy.restore(); }); -}); -describe('renderReact client side endpoint', () => { - let result; - beforeEach(() => { - result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - }); + it('calls renderReactClient', (done) => { + const result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - it('exists', () => { - assert.isFunction(renderReactClient); - assert.equal(renderReactClient.length, 2); - }); - - ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => { jsdom.env(result, (err, window) => { if (err) { done(err); return; } - global.window = window; - global.document = window.document; - - const hydrateMethod = sinon.spy(ReactDOM, 'hydrate'); - - // Calling it again for the client. - renderReactClient('ExampleReactComponent', ExampleReactComponent); - - assert(hydrateMethod.calledOnce); - - hydrateMethod.restore(); - - delete global.window; - delete global.document; - - done(); - }); - }); - - it('calls hypernova.client (render method)', (done) => { - jsdom.env(result, (err, window) => { - if (err) { - done(err); - return; - } - - const sandbox = sinon.createSandbox(); - if (ReactDOM.hydrate) { - sandbox.stub(ReactDOM, 'hydrate').value(undefined); - } - - const renderMethod = sinon.spy(ReactDOM, 'render'); + const renderReactClientSpy = sinon.spy(renderReactClientModule, 'renderReactClient'); global.window = window; global.document = window.document; // Calling it again for the client. - renderReactClient('ExampleReactComponent', ExampleReactComponent); - - assert(renderMethod.calledOnce); + renderReact('ExampleReactComponent', ExampleReactComponent); - sandbox.restore(); + assert(renderReactClientSpy.calledOnce, `renderReactClient was not called once but ${renderReactClientSpy.callCount} times`); - renderMethod.restore(); + renderReactClientSpy.restore(); delete global.window; delete global.document; @@ -154,17 +51,3 @@ describe('renderReact client side endpoint', () => { }); }); }); - -describe('renderReact server side endpoint', () => { - it('exists', () => { - assert.isFunction(renderReactServer); - assert.equal(renderReactServer.length, 2); - }); - - it('has correct markup on server', () => { - const result = renderReactServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - - assert.isString(result); - assert.match(result, /Hello Desmond/); - }); -}); diff --git a/test/renderReactClient-test.js b/test/renderReactClient-test.js new file mode 100644 index 0000000..17f0735 --- /dev/null +++ b/test/renderReactClient-test.js @@ -0,0 +1,87 @@ +import jsdom from 'jsdom'; +import { assert } from 'chai'; +import sinon from 'sinon'; +import ReactDOM from 'react-dom'; +import ifReact from 'enzyme-adapter-react-helper/build/ifReact'; + +import ExampleReactComponent from './components/ExampleReactComponent'; +import { renderReactServer } from '../lib/server'; +import { renderReact, renderReactClient } from '../lib/client'; + +describe('client', () => { + let result; + beforeEach(() => { + result = renderReactServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); + + assert.isString(result); + assert.match(result, /Hello Desmond/); + }); + + it('renderReact exists', () => { + assert.isFunction(renderReact); + assert.equal(renderReact.length, 2); + }); + + it('renderReactClient exists', () => { + assert.isFunction(renderReactClient); + assert.equal(renderReactClient.length, 2); + }); + + ifReact('>= 16', it, it.skip)('calls hypernova.client (hydrate method)', (done) => { + jsdom.env(result, (err, window) => { + if (err) { + done(err); + return; + } + + global.window = window; + global.document = window.document; + + const hydrateMethod = sinon.spy(ReactDOM, 'hydrate'); + + // Calling it again for the client. + renderReact('ExampleReactComponent', ExampleReactComponent); + + assert(hydrateMethod.calledOnce); + + hydrateMethod.restore(); + + delete global.window; + delete global.document; + + done(); + }); + }); + + it('calls hypernova.client (render method)', (done) => { + jsdom.env(result, (err, window) => { + if (err) { + done(err); + return; + } + + const sandbox = sinon.createSandbox(); + if (ReactDOM.hydrate) { + sandbox.stub(ReactDOM, 'hydrate').value(undefined); + } + + const renderMethod = sinon.spy(ReactDOM, 'render'); + + global.window = window; + global.document = window.document; + + // Calling it again for the client. + renderReact('ExampleReactComponent', ExampleReactComponent); + + assert(renderMethod.calledOnce); + + sandbox.restore(); + renderMethod.restore(); + + delete global.window; + delete global.document; + + done(); + }); + }); +}); diff --git a/test/renderReactServer-test.js b/test/renderReactServer-test.js new file mode 100644 index 0000000..8b0b77e --- /dev/null +++ b/test/renderReactServer-test.js @@ -0,0 +1,26 @@ +import { assert } from 'chai'; + +import ExampleReactComponent from './components/ExampleReactComponent'; +import { renderReact, renderReactServer } from '../lib/server'; + +describe('server renderReact', () => { + let result; + beforeEach(() => { + result = renderReact('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); + }); + + it('exists', () => { + assert.isFunction(renderReact); + assert.equal(renderReact.length, 2); + }); + + it('exists', () => { + assert.isFunction(renderReactServer); + assert.equal(renderReactServer.length, 2); + }); + + it('has correct markup', () => { + assert.isString(result); + assert.match(result, /Hello Desmond/); + }); +}); diff --git a/test/renderReactStatic-test.js b/test/renderReactStatic-test.js index ccf0819..d7dd4d9 100644 --- a/test/renderReactStatic-test.js +++ b/test/renderReactStatic-test.js @@ -1,56 +1,23 @@ import { assert } from 'chai'; +import sinon from 'sinon'; import ExampleReactComponent from './components/ExampleReactComponent'; import { renderReactStatic } from '..'; -import { renderReactStatic as renderReactStaticServer } from '../lib/server'; +import * as renderReactServerModule from '../lib/server'; describe('renderReactStatic', () => { - let result; - beforeEach(() => { - result = renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); - }); - it('exists', () => { assert.isFunction(renderReactStatic); assert.equal(renderReactStatic.length, 2); }); - it('has correct markup on server', () => { - assert.isString(result); - assert.match(result, /Hello Zack/); - }); -}); - -describe('renderReactStatic server side endpoint', () => { - let result; - beforeEach(() => { - result = renderReactStaticServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); - }); + it('calls renderReactStaticServer', () => { + const renderReactStaticServerSpy = sinon.spy(renderReactServerModule, 'renderReactStaticServer'); - it('exists', () => { - assert.isFunction(renderReactStaticServer); - assert.equal(renderReactStaticServer.length, 2); - }); + renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Desmond' }); - it('has correct markup on server', () => { - assert.isString(result); - assert.match(result, /Hello Zack/); - }); -}); - -describe('renderReactStatic server side endpoint', () => { - let result; - beforeEach(() => { - result = renderReactStaticServer('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); - }); - - it('exists', () => { - assert.isFunction(renderReactStaticServer); - assert.equal(renderReactStaticServer.length, 2); - }); + assert(renderReactStaticServerSpy.calledOnce, `renderReactServer was not called once but ${renderReactStaticServerSpy.callCount} times`); - it('has correct markup on server', () => { - assert.isString(result); - assert.match(result, /Hello Zack/); + renderReactStaticServerSpy.restore(); }); }); diff --git a/test/renderReactStaticServer-test.js b/test/renderReactStaticServer-test.js new file mode 100644 index 0000000..8937727 --- /dev/null +++ b/test/renderReactStaticServer-test.js @@ -0,0 +1,18 @@ +import { assert } from 'chai'; + +import ExampleReactComponent from './components/ExampleReactComponent'; +import { renderReactStatic } from '../lib/server'; + +describe('server renderReactStatic', () => { + it('exists', () => { + assert.isFunction(renderReactStatic); + assert.equal(renderReactStatic.length, 2); + }); + + it('has correct markup', () => { + const result = renderReactStatic('ExampleReactComponent', ExampleReactComponent)({ name: 'Zack' }); + + assert.isString(result); + assert.match(result, /Hello Zack/); + }); +}); From 681dbf35689acf79356e3b48b605b43d490b154c Mon Sep 17 00:00:00 2001 From: Wing Leung Date: Thu, 26 Jul 2018 10:56:39 +0200 Subject: [PATCH 3/3] fix eslint error --- src/server.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server.js b/src/server.js index fcb474e..ff6e823 100644 --- a/src/server.js +++ b/src/server.js @@ -3,11 +3,14 @@ import ReactDOMServer from 'react-dom/server'; import hypernova, { serialize } from 'hypernova'; const renderReactServer = (name, component) => (props) => { - const contents = ReactDOMServer.renderToString(React.createElement(component, props)); + const contents = ReactDOMServer.renderToString( + React.createElement(component, props), + ); return serialize(name, contents, props); }; const renderReactStaticServer = (name, component) => props => + // eslint-disable-next-line implicit-arrow-linebreak ReactDOMServer.renderToStaticMarkup(React.createElement(component, props)); const renderReact = (name, component) => hypernova({