Skip to content

Commit

Permalink
Initial Import
Browse files Browse the repository at this point in the history
  • Loading branch information
goatslacker authored and ljharb committed Jun 4, 2016
1 parent ebfe4c4 commit 43f7359
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["airbnb"]
}
11 changes: 11 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"root": true,
"extends": "airbnb",
"env": {
"es6": true
},
"ecmaFeatures": {
"modules": true,
"jsx": true
}
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ node_modules

# Optional REPL history
.node_repl_history

TODO
coverage
lib
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# hypernova-react
# hypernova-react

[React](https://github.com/facebook/react) bindings for [Hypernova](https://github.com/airbnb/hypernova).

On the server, wraps the component in a function to render it to a HTML string given its props.

On the client, calling this function with your component scans the DOM for any server-side rendered instances of it. It then resumes those components using the server-specified props.

## Install

```sh
npm install hypernova-react
```

## Usage

Here's how use use it in your module:

```js
import { renderReact } from 'hypernova-react';
import MyComponent from './src/MyComponent.jsx';

export default renderReact(
'MyComponent.hypernova.js', // this file's name (or really any unique name)
MyComponent,
);
```
63 changes: 63 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "hypernova-react",
"version": "1.0.0",
"description": "React bindings for Hypernova",
"main": "lib/index.js",
"scripts": {
"prepublish": "npm run build",
"build": "rimraf lib && babel src -d lib",
"lint": "eslint src test",
"test": "npm run lint && npm run build && npm run test:coverage",
"tests-only": "npm run build && npm run test:quick",
"test:coverage": "babel-node node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- -R tap test/*-test.js",
"test:quick": "babel-node node_modules/.bin/_mocha -R tap test/*-test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/airbnb/hypernova-react.git"
},
"keywords": [
"react",
"hypernova",
"server",
"render",
"isomorphic",
"universal"
],
"files": [
"README.md",
"lib",
"src",
"test",
".eslintrc",
".babelrc"
],
"author": "Josh Perez <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/airbnb/hypernova-react/issues"
},
"homepage": "https://github.com/airbnb/hypernova-react#readme",
"peerDependencies": {
"react": "0.14.x || >= 15.x",
"react-dom": "0.14.x || >= 15.x"
},
"devDependencies": {
"babel-cli": "^6.9.0",
"babel-preset-airbnb": "^2.0.0",
"chai": "^3.5.0",
"eslint": "^2.11.1",
"eslint-config-airbnb": "^9.0.1",
"eslint-plugin-import": "^1.8.1",
"eslint-plugin-jsx-a11y": "^1.2.3",
"eslint-plugin-react": "^5.1.1",
"istanbul": "^0.4.3",
"jsdom": "^9.2.1",
"mocha": "^2.5.3",
"react": "^15.1.0",
"react-dom": "^15.1.0",
"rimraf": "^2.5.2",
"sinon": "^1.17.4",
"sinon-sandbox": "^1.0.2"
}
}
32 changes: 32 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
import hypernova, { serialize, load } 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() {
const { node, data } = load(name);

if (node) {
const element = React.createElement(component, data);
ReactDOM.render(element, node);
}

return component;
},
});

export const renderReactStatic = (name, component) => hypernova({
server() {
return props => ReactDOMServer.renderToStaticMarkup(React.createElement(component, props));
},

client() {},
});
5 changes: 5 additions & 0 deletions test/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"env": {
"mocha": true
}
}
12 changes: 12 additions & 0 deletions test/components/ExampleReactComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const React = require('react');

function ExampleReactComponent(props) {
const name = ['Hello', props.name];
return React.createElement('div', { id: 'example' }, name.join(' '));
}

ExampleReactComponent.propTypes = {
name: React.PropTypes.string,
};

module.exports = ExampleReactComponent;
4 changes: 4 additions & 0 deletions test/components/HypernovaExampleReact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const renderReact = require('../../').renderReact;
const Component = require('./ExampleReactComponent.js');

module.exports = renderReact('HypernovaExampleReact.js', Component);
5 changes: 5 additions & 0 deletions test/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sinon from 'sinon-sandbox';

afterEach(() => {
sinon.restore();
});
41 changes: 41 additions & 0 deletions test/renderReact-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import ExampleReactComponent from './components/ExampleReactComponent';
import jsdom from 'jsdom';
import { assert } from 'chai';
import { renderReact } from '..';

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/);
});

it('calls hypernova.client', (done) => {
jsdom.env(result, (err, window) => {
if (err) {
done(err);
return;
}

global.window = window;
global.document = window.document;

// Calling it again for the client.
renderReact('ExampleReactComponent', ExampleReactComponent);

delete global.window;
delete global.document;

done();
});
});
});
20 changes: 20 additions & 0 deletions test/renderReactStatic-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import ExampleReactComponent from './components/ExampleReactComponent';
import { assert } from 'chai';
import { renderReactStatic } from '..';

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/);
});
});

0 comments on commit 43f7359

Please sign in to comment.