├── .eslintignore ├── .eslintrc.js ├── .githooks └── pre-commit │ └── test ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── cache.js ├── context.js ├── es6.jsx ├── mixins.js ├── simple.js └── stateless-function.js ├── package.json ├── src ├── index.js └── utils │ ├── children.js │ ├── extend.js │ └── isObject.js ├── test ├── index.js └── utils │ ├── children.js │ ├── extend.js │ └── isObject.js └── tools └── benchmark.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | coverage 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'loris/es5', 3 | root: true, 4 | env: { 5 | node: true, 6 | mocha: true 7 | }, 8 | rules: { 9 | strict: 'off', 10 | 'no-unused-vars': ['error', {args: 'none'}], 11 | 'no-console': ['warn'] 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.githooks/pre-commit/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # * Check changed js files using eslint. 4 | # 5 | 6 | PATCH_FILE="working-tree.patch" 7 | NPM_BIN="./node_modules/.bin" 8 | 9 | function cleanup { 10 | exit_code=$? 11 | if [ -f "$PATCH_FILE" ]; then 12 | git apply "$PATCH_FILE" 2> /dev/null 13 | rm "$PATCH_FILE" 14 | fi 15 | exit $exit_code 16 | } 17 | 18 | trap cleanup EXIT SIGINT SIGHUP 19 | 20 | # Cancel any changes to the working tree that are not going to be committed 21 | git diff > "$PATCH_FILE" 22 | git checkout -- . 23 | 24 | git_cached_files=$(git diff --cached --name-only --diff-filter=ACMR | grep "\.js$") 25 | if [ "$git_cached_files" ]; then 26 | echo "Start linting..." 27 | 28 | $NPM_BIN/eslint $git_cached_files --quiet || exit 1 29 | 30 | echo "There are no errors in codestyle." 31 | fi 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | coverage 4 | .DS_Store 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "5" 5 | - "4" 6 | script: 7 | - npm run lint 8 | - npm run test-coveralls 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Andrey Morozov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | With the release of the new version [React](https://reactjs.org/blog/2017/09/26/react-v16.0.html#better-server-side-rendering) and Node.js the performance issue has ceased to be so acute. Use last versions of React and Node.js for better performance. 2 | ====== 3 | 4 | # React Server [![Build Status](https://travis-ci.org/alt-j/fast-react-server.svg?branch=master)](https://travis-ci.org/alt-j/react-server) [![Coverage Status](https://coveralls.io/repos/github/alt-j/fast-react-server/badge.svg?branch=master)](https://coveralls.io/github/alt-j/fast-react-server?branch=master) 5 | 6 | It's high speed react mock for server rendering. 7 | You can use it with [fast react render](https://github.com/alt-j/fast-react-render), in that case render will be **12 times as fast** (see [benchmarks](https://github.com/alt-j/react-server-benchmark)) as [traditional react rendering](https://facebook.github.io/react/docs/environments.html) (in production mode). 8 | 9 | ## Quick start 10 | ```sh 11 | npm install fast-react-render fast-react-server 12 | ``` 13 | 14 | ```js 15 | var React = require('fast-react-server'); 16 | var ReactRender = require('fast-react-render'); 17 | 18 | var element = React.createElement( 19 | React.createClass({ 20 | render: function () { 21 | return React.createElement('div', {}, this.props.text); 22 | } 23 | }), 24 | {text: 'some text'} 25 | ); 26 | console.log(ReactRender.elementToString(element)); 27 | ``` 28 | 29 | If you want use it, you must remember: each component, which you want render, you must declared with this mock (configure you build system for that). 30 | 31 | Also fast react server support [ES6 classes](https://facebook.github.io/react/docs/reusable-components.html#es6-classes) and [Stateless Functions](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) (see examples: [es6](examples/es6.jsx) and [stateless-function](examples/stateless-function.js)). 32 | 33 | More examples: 34 | - tiny [seed project](https://github.com/alt-j/fast-react-seed) (in progress); 35 | - [examples](examples/) for main features. 36 | 37 | ## Cache 38 | Fast react server support cache for component which implement in [fast react render](https://github.com/alt-j/fast-react-render). 39 | See how it use [here](https://github.com/alt-j/fast-react-render#cache). 40 | -------------------------------------------------------------------------------- /examples/cache.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | var React = require('../src/index'); 4 | var ReactRender = require('fast-react-render'); 5 | 6 | var LRU = require('lru-cache'); 7 | var cache = LRU({ 8 | max: 500, 9 | maxAge: 60 * 60 10 | }); 11 | 12 | // Transform all es6 to es5 and jsx to js, before execution. 13 | var Component = React.createClass({ 14 | displayName: 'Component', 15 | 16 | getDefaultProps: function () { 17 | return { 18 | content: 'Some bold text' 19 | }; 20 | }, 21 | 22 | getCacheKey: function () { 23 | return this.props.content; 24 | }, 25 | 26 | render: function () { 27 | return React.createElement('div', { 28 | className: 'text', 29 | dangerouslySetInnerHTML: {__html: this.props.content} 30 | }); 31 | } 32 | }); 33 | 34 | console.log( 35 | ReactRender.elementToString( 36 | React.createElement(Component), 37 | {cache: cache} 38 | ) 39 | ); 40 | 41 | // Render from cache 42 | console.log( 43 | ReactRender.elementToString( 44 | React.createElement(Component, {content: 'Some bold text'}), 45 | {cache: cache} 46 | ) 47 | ); 48 | -------------------------------------------------------------------------------- /examples/context.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | var React = require('../src/index'); 4 | var ReactRender = require('fast-react-render'); 5 | 6 | // Transform all es6 to es5 and jsx to js, before execution. 7 | var ComponentInner = React.createClass({ 8 | render: function () { 9 | return React.createElement('span', {className: 'final'}, this.context.counter); 10 | } 11 | }); 12 | 13 | var Component = React.createClass({ 14 | getChildContext: function () { 15 | return { 16 | counter: this.context.counter + 1 17 | }; 18 | }, 19 | 20 | render: function () { 21 | return React.createElement( 22 | 'span', 23 | null, 24 | this.context.counter + ', ', 25 | React.createElement(ComponentInner) 26 | ); 27 | } 28 | }); 29 | 30 | var ComponentWrapper = React.createClass({ 31 | getDefaultProps: function () { 32 | return { 33 | content: 'Chain: ' 34 | }; 35 | }, 36 | 37 | getChildContext: function () { 38 | return { 39 | counter: 1 40 | }; 41 | }, 42 | 43 | render: function () { 44 | return React.createElement( 45 | 'div', 46 | {}, 47 | this.props.content + this.context.counter + ', ', 48 | React.createElement(Component) 49 | ); 50 | } 51 | }); 52 | 53 | console.log( 54 | ReactRender.elementToString( 55 | React.createElement(ComponentWrapper), 56 | { 57 | context: { 58 | counter: 0 59 | } 60 | } 61 | ) 62 | ); 63 | -------------------------------------------------------------------------------- /examples/es6.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import React from '../src/index'; 4 | import ReactRender from 'fast-react-render'; 5 | 6 | // Transform all es6 to es5 and jsx to js, before execution. 7 | class Text extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = {text: this.props.text}; 11 | } 12 | 13 | static defaultProps = { 14 | text: 'some text here' 15 | } 16 | 17 | render() { 18 | return
{this.state.text}
; 19 | } 20 | } 21 | 22 | const element = React.createElement(Text, {text: 'test'}); 23 | console.log(ReactRender.elementToString(element)); 24 | 25 | -------------------------------------------------------------------------------- /examples/mixins.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | var React = require('../src/index'); 4 | var ReactRender = require('fast-react-render'); 5 | 6 | // Transform all es6 to es5 and jsx to js, before execution. 7 | var ComponentBase = { 8 | getDefaultProps: function () { 9 | return { 10 | text: 'Some bold text' 11 | }; 12 | }, 13 | 14 | renderText: function () { 15 | return React.createElement('div', { 16 | className: 'text', 17 | dangerouslySetInnerHTML: {__html: this.props.text} 18 | }); 19 | } 20 | }; 21 | 22 | var Component = React.createClass({ 23 | mixins: [ComponentBase], 24 | 25 | render: function () { 26 | return React.createElement('div', null, this.renderText()); 27 | } 28 | }); 29 | 30 | var element = React.createElement(Component); 31 | console.log(ReactRender.elementToString(element)); 32 | -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | var React = require('../src/index'); 4 | var ReactRender = require('fast-react-render'); 5 | 6 | // Transform all es6 to es5 and jsx to js, before execution. 7 | var Component = React.createClass({ 8 | getDefaultProps: function () { 9 | return { 10 | content: 'Some bold text' 11 | }; 12 | }, 13 | 14 | render: function () { 15 | return React.createElement('div', { 16 | className: 'text', 17 | dangerouslySetInnerHTML: {__html: this.props.content} 18 | }); 19 | } 20 | }); 21 | 22 | var element = React.createElement(Component); 23 | console.log(ReactRender.elementToString(element)); 24 | -------------------------------------------------------------------------------- /examples/stateless-function.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | var React = require('../src/index'); 4 | var ReactRender = require('fast-react-render'); 5 | 6 | // Transform all es6 to es5 and jsx to js, before execution. 7 | function Text(props) { 8 | return React.createElement('div', {className: 'text'}, props.text); 9 | } 10 | 11 | var element = React.createElement(Text, {text: 'test'}); 12 | console.log(ReactRender.elementToString(element)); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-react-server", 3 | "version": "1.5.0", 4 | "description": "Fast server react (advanced performance render)", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/alt-j/fast-react-server.git" 9 | }, 10 | "keywords": [ 11 | "react", 12 | "render", 13 | "server", 14 | "performance", 15 | "optimization", 16 | "speed", 17 | "cache", 18 | "html" 19 | ], 20 | "author": "Andrey Morozov ", 21 | "license": "MIT", 22 | "publishConfig": { 23 | "registry": "https://registry.npmjs.org/" 24 | }, 25 | "scripts": { 26 | "test": "npm run lint && npm run test-cov", 27 | "test-cov": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- -R spec --recursive --check-leaks", 28 | "test-coveralls": "npm run test-cov && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", 29 | "lint": "./node_modules/.bin/eslint . --quiet", 30 | "benchmark": "node ./tools/benchmark" 31 | }, 32 | "dependencies": { 33 | "prop-types": "15.5.10" 34 | }, 35 | "devDependencies": { 36 | "chai": "3.5.0", 37 | "coveralls": "2.11.12", 38 | "eslint": "2.5.3", 39 | "eslint-config-loris": "5.1.0", 40 | "fast-react-benchmark": "1.2.0", 41 | "fast-react-render": "1.1.2", 42 | "git-hooks": "1.1.0", 43 | "istanbul": "0.4.5", 44 | "lru-cache": "4.0.1", 45 | "mocha": "3.0.2", 46 | "mocha-lcov-reporter": "1.2.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var PropTypes = require('prop-types'); 2 | 3 | var children = require('./utils/children'); 4 | var extend = require('./utils/extend'); 5 | var isObject = require('./utils/isObject'); 6 | 7 | var FastReactServer = { 8 | Component: function (props, context) { 9 | this.props = props; 10 | this.context = context; 11 | }, 12 | 13 | Children: children, 14 | 15 | PropTypes: PropTypes, 16 | 17 | /** 18 | * @param {Object} decl React component declaration. 19 | * @returns {Function} 20 | */ 21 | createClass: function (decl) { 22 | var mixins = Array.isArray(decl.mixins) ? decl.mixins : []; 23 | var proto = extend.apply(this, mixins.concat([decl])); 24 | 25 | proto.setState = function (data) { 26 | this.state = extend(this.state, data); 27 | }; 28 | 29 | var Component = function (props, context) { 30 | this.props = props; 31 | this.context = context; 32 | 33 | this.state = this.getInitialState ? this.getInitialState() : {}; 34 | }; 35 | 36 | Component.defaultProps = proto.getDefaultProps ? proto.getDefaultProps() : {}; 37 | 38 | if (decl.statics) { 39 | for (var method in decl.statics) { 40 | Component[method] = decl.statics[method]; 41 | } 42 | } 43 | 44 | Component.prototype = extend(proto); 45 | 46 | return Component; 47 | }, 48 | 49 | /** 50 | * @param {Function|String} type 51 | * @param {Object} [props] 52 | * @param {...String} [child] 53 | * @returns {RenderElement} element 54 | */ 55 | createElement: function (type, props, child) { 56 | var children = child || props && props.children; 57 | 58 | var i = arguments.length; 59 | if (i > 3) { 60 | children = []; 61 | 62 | while (i-- > 2) { 63 | children[i - 2] = arguments[i]; 64 | } 65 | } 66 | 67 | return { 68 | type: type, 69 | props: extend(type && type.defaultProps, props, {children: children}) 70 | }; 71 | }, 72 | 73 | /** 74 | * @param {RenderElement} element 75 | * @param {Object} [props] 76 | * @param {...String} [child] 77 | * @returns {RenderElement} newElement 78 | */ 79 | cloneElement: function (element, props) { 80 | var newArgs = [element.type, extend(element.props, props)]; 81 | 82 | var i = arguments.length; 83 | while (i-- > 2) { 84 | newArgs[i] = arguments[i]; 85 | } 86 | 87 | return this.createElement.apply(this, newArgs); 88 | }, 89 | 90 | /** 91 | * @param {*} element 92 | * @returns {Boolean} isValid 93 | */ 94 | isValidElement: function (element) { 95 | return element === null || 96 | (isObject(element) && element.hasOwnProperty('type') && isObject(element.props)); 97 | } 98 | }; 99 | 100 | FastReactServer.PureComponent = FastReactServer.Component; 101 | FastReactServer.PropTypes.element = FastReactServer.PropTypes.instanceOf(FastReactServer.Component); 102 | FastReactServer.PropTypes.node = FastReactServer.PropTypes.object; 103 | 104 | module.exports = FastReactServer; 105 | -------------------------------------------------------------------------------- /src/utils/children.js: -------------------------------------------------------------------------------- 1 | var extend = require('./extend'); 2 | 3 | module.exports = { 4 | /** 5 | * @param {Object[]|Object} [children] 6 | * @returns {Object[]} children 7 | */ 8 | toArray: function (children) { 9 | var childrenArray = toArray(children); 10 | 11 | var result = []; 12 | 13 | var i = childrenArray.length; 14 | while (--i >= 0) { 15 | result[i] = extend({key: i}, childrenArray[i]); 16 | } 17 | 18 | return result; 19 | }, 20 | 21 | /** 22 | * @param {Object[]|Object} [children] 23 | * @returns {Number} count 24 | */ 25 | count: function (children) { 26 | return Array.isArray(children) ? children.length : (children ? 1 : 0); 27 | }, 28 | 29 | /** 30 | * @param {Object[]|Object} [children] 31 | * @returns {Object|null} children 32 | */ 33 | only: function (children) { 34 | return !Array.isArray(children) ? children : null; 35 | }, 36 | 37 | /** 38 | * @param {Object[]|Object} [children] 39 | * @param {Function} callback 40 | * @param {Function} [thisArg] 41 | * @returns {*} result 42 | */ 43 | map: function (children, callback, thisArg) { 44 | if (!children) { 45 | return children; 46 | } 47 | 48 | var childrenArray = toArray(children); 49 | 50 | var result = []; 51 | 52 | var i = childrenArray.length; 53 | while (--i >= 0) { 54 | result[i] = callback.call(thisArg || this, childrenArray[i], i); 55 | } 56 | 57 | return result; 58 | }, 59 | 60 | /** 61 | * @param {Object[]|Object} [children] 62 | * @param {Function} callback 63 | * @param {Function} [thisArg] 64 | */ 65 | forEach: function (children, callback, thisArg) { 66 | if (!children) { 67 | return children; 68 | } 69 | 70 | var childrenArray = toArray(children); 71 | 72 | for (var i = 0; i < childrenArray.length; i++) { 73 | callback.call(thisArg || this, childrenArray[i], i); 74 | } 75 | } 76 | }; 77 | 78 | /** 79 | * @param {Object[]|Object} [children] 80 | * @returns {Object[]} children 81 | */ 82 | function toArray(children) { 83 | return Array.isArray(children) ? children : (children ? [children] : []); 84 | } 85 | -------------------------------------------------------------------------------- /src/utils/extend.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {...Object} source 3 | * @returns {Object} result 4 | */ 5 | module.exports = function (source) { 6 | var result = {}; 7 | 8 | var i = 0; 9 | while (i < arguments.length) { 10 | var object = arguments[i++]; 11 | 12 | for (var key in object) { 13 | result[key] = object[key]; 14 | } 15 | } 16 | 17 | return result; 18 | }; 19 | -------------------------------------------------------------------------------- /src/utils/isObject.js: -------------------------------------------------------------------------------- 1 | module.exports = function (object) { 2 | return object !== null && typeof object === 'object' && !Array.isArray(object); 3 | }; 4 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | 4 | var React = require('../src/index'); 5 | 6 | describe('React', function () { 7 | describe('Component', function () { 8 | it('should be a function', function () { 9 | expect(React.Component).to.be.a('function'); 10 | }); 11 | 12 | it('should transform params to fields', function () { 13 | var props = {a: 1}; 14 | var context = {b: 2}; 15 | 16 | var component = new React.Component(props, context); 17 | 18 | expect(component.props).to.equal(props); 19 | expect(component.context).to.equal(context); 20 | }); 21 | }); 22 | 23 | describe('PureComponent', function () { 24 | it('should be a function', function () { 25 | expect(React.PureComponent).to.be.a('function'); 26 | }); 27 | 28 | it('should transform params to fields', function () { 29 | var props = {a: 1}; 30 | var context = {b: 2}; 31 | 32 | var component = new React.PureComponent(props, context); 33 | 34 | expect(component.props).to.equal(props); 35 | expect(component.context).to.equal(context); 36 | }); 37 | }); 38 | 39 | describe('createClass', function () { 40 | it('should be a function', function () { 41 | expect(React.createClass).to.be.a('function'); 42 | }); 43 | 44 | it('should return class', function () { 45 | expect(React.createClass({})).to.be.a('function'); 46 | }); 47 | 48 | it('should transfer method from decl to class prototype', function () { 49 | var decl = { 50 | render: function () {} 51 | }; 52 | var instance = new (React.createClass(decl))(); 53 | 54 | expect(instance.render).to.equal(decl.render); 55 | }); 56 | 57 | it('should transfer method and property from decl.statics to class', function () { 58 | var decl = { 59 | statics: { 60 | someProperty: 1, 61 | someMethod: function () {} 62 | } 63 | }; 64 | var SomeClass = React.createClass(decl); 65 | 66 | expect(SomeClass.someProperty).to.equal(decl.statics.someProperty); 67 | expect(SomeClass.someMethod).to.equal(decl.statics.someMethod); 68 | }); 69 | 70 | it('should execute getDefaultProps method and put result to static component field', function () { 71 | var defaultProps = {a: 1}; 72 | var Component = React.createClass({ 73 | getDefaultProps: function () { 74 | return defaultProps; 75 | } 76 | }); 77 | 78 | expect(Component.defaultProps).to.equal(defaultProps); 79 | }); 80 | 81 | it('should mix all mixins to prototype', function () { 82 | var mixin = {someMethod: function () {}}; 83 | var Component = React.createClass({mixins: [mixin]}); 84 | 85 | expect(Component.prototype.someMethod).to.equal(mixin.someMethod); 86 | }); 87 | 88 | it('should put getInitialState result to instance state', function () { 89 | var Component = React.createClass({ 90 | getInitialState: function () { 91 | return {a: 1}; 92 | } 93 | }); 94 | var instance = new Component(); 95 | 96 | expect(instance.state.a).to.equal(1); 97 | }); 98 | 99 | it('should call getInitialState only after props received', function () { 100 | var Component = React.createClass({ 101 | getInitialState: function () { 102 | return {b: this.props.a}; 103 | } 104 | }); 105 | var instance = new Component({a: 1}); 106 | 107 | expect(instance.state.b).to.equal(1); 108 | }); 109 | 110 | it('should change state after setState called', function () { 111 | var instance = new (React.createClass({}))(); 112 | instance.setState({b: 2}); 113 | 114 | expect(instance.state.b).to.equal(2); 115 | }); 116 | }); 117 | 118 | describe('createElement', function () { 119 | it('should be a function', function () { 120 | expect(React.createElement).to.be.a('function'); 121 | }); 122 | 123 | it('should transfer to element all props', function () { 124 | var element = React.createElement('div', {a: 1, b: 2}); 125 | 126 | expect(element.props.a).to.equal(1); 127 | expect(element.props.b).to.equal(2); 128 | }); 129 | 130 | it('should mix default props to props', function () { 131 | var Component = React.createClass({ 132 | getDefaultProps: function () { 133 | return {a: 1}; 134 | } 135 | }); 136 | 137 | var element = React.createElement(Component, {b: 2}); 138 | 139 | expect(element.props.a).to.equal(1); 140 | expect(element.props.b).to.equal(2); 141 | }); 142 | 143 | it('should put child to props', function () { 144 | var element = React.createElement('div', null, 'child'); 145 | expect(element.props.children).to.equal('child'); 146 | }); 147 | 148 | it('should put children to props', function () { 149 | var element = React.createElement('div', null, 'first child', 'second child'); 150 | 151 | expect(element.props.children[0]).to.equal('first child'); 152 | expect(element.props.children[1]).to.equal('second child'); 153 | }); 154 | 155 | it('should put all arguments starting from the second to children', function () { 156 | var element = React.createElement('div', null, 'first child', 'second child', 'third child'); 157 | expect(element.props.children.length).to.equal(3); 158 | 159 | expect(element.props.children[0]).to.equal('first child'); 160 | expect(element.props.children[1]).to.equal('second child'); 161 | expect(element.props.children[2]).to.equal('third child'); 162 | }); 163 | 164 | it('should not overwrite children from props in case of third argument is undefined', function () { 165 | var element = React.createElement('div', {children: 'child'}); 166 | expect(element.props.children).to.equal('child'); 167 | }); 168 | }); 169 | 170 | describe('cloneElement', function () { 171 | it('should be a function', function () { 172 | expect(React.cloneElement).to.be.a('function'); 173 | }); 174 | 175 | it('should transfer to element all props', function () { 176 | var originalElement = React.createElement('div', {a: 1, b: 2}); 177 | var element = React.cloneElement(originalElement); 178 | 179 | expect(element.props.a).to.equal(1); 180 | expect(element.props.b).to.equal(2); 181 | }); 182 | 183 | it('should mix props from original element and from params', function () { 184 | var originalElement = React.createElement('div', {a: 1, b: 2}); 185 | var element = React.cloneElement(originalElement, {b: 3, c: 4}); 186 | 187 | expect(element.props.a).to.equal(1); 188 | expect(element.props.b).to.equal(3); 189 | expect(element.props.c).to.equal(4); 190 | }); 191 | 192 | it('should replace existing children', function () { 193 | var originalElement = React.createElement('div', null, 'original child'); 194 | var element = React.cloneElement(originalElement, null, 'child'); 195 | 196 | expect(element.props.children).to.equal('child'); 197 | }); 198 | 199 | it('should put all arguments starting from the second to children', function () { 200 | var originalElement = React.createElement('div', null); 201 | var element = React.cloneElement(originalElement, null, 'first child', 'second child', 'third child'); 202 | 203 | expect(element.props.children.length).to.equal(3); 204 | 205 | expect(element.props.children[0]).to.equal('first child'); 206 | expect(element.props.children[1]).to.equal('second child'); 207 | expect(element.props.children[2]).to.equal('third child'); 208 | }); 209 | 210 | it('should retain children from original element', function () { 211 | var originalElement = React.createElement('div', null, 'child'); 212 | var element = React.cloneElement(originalElement); 213 | 214 | expect(element.props.children).to.equal('child'); 215 | }); 216 | }); 217 | 218 | describe('isValidElement', function () { 219 | it('should be a function', function () { 220 | expect(React.isValidElement).to.be.a('function'); 221 | }); 222 | 223 | it('should validate null', function () { 224 | expect(React.isValidElement(null)).to.equal(true); 225 | }); 226 | 227 | it('should validate object with type and props', function () { 228 | expect(React.isValidElement({type: React.Component, props: {}})).to.equal(true); 229 | }); 230 | 231 | it('should not validate other stuff', function () { 232 | expect(React.isValidElement({})).to.equal(false); 233 | expect(React.isValidElement({type: {}})).to.equal(false); 234 | expect(React.isValidElement({props: {}})).to.equal(false); 235 | expect(React.isValidElement(1)).to.equal(false); 236 | expect(React.isValidElement([])).to.equal(false); 237 | expect(React.isValidElement()).to.equal(false); 238 | }); 239 | }); 240 | }); 241 | -------------------------------------------------------------------------------- /test/utils/children.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | 4 | var children = require('../../src/utils/children'); 5 | 6 | var child = {key: 'some value'}; 7 | var oneInArray = [{}]; 8 | var twoInArray = [{}, {}]; 9 | 10 | describe('children', function () { 11 | describe('toArray', function () { 12 | it('should be a function', function () { 13 | expect(children.toArray).to.be.a('function'); 14 | }); 15 | 16 | it('should return array for children', function () { 17 | var result = children.toArray(twoInArray); 18 | 19 | expect(Array.isArray(result)).to.equal(true); 20 | expect(result.length).to.equal(2); 21 | }); 22 | 23 | it('should return array for child', function () { 24 | var result = children.toArray(child); 25 | 26 | expect(Array.isArray(result)).to.equal(true); 27 | expect(result.length).to.equal(1); 28 | }); 29 | 30 | it('should return array for null/undefined', function () { 31 | var result1 = children.toArray(null); 32 | var result2 = children.toArray(undefined); 33 | 34 | expect(Array.isArray(result1)).to.equal(true); 35 | expect(Array.isArray(result2)).to.equal(true); 36 | 37 | expect(result1.length).to.equal(0); 38 | expect(result2.length).to.equal(0); 39 | }); 40 | 41 | it('should add key to each child', function () { 42 | var result = children.toArray(twoInArray); 43 | 44 | expect(result[0].key).to.be.a('number'); 45 | expect(result[1].key).to.be.a('number'); 46 | }); 47 | 48 | it('should not override child key', function () { 49 | var result = children.toArray(child); 50 | 51 | expect(result[0].key).to.equal(child.key); 52 | }); 53 | }); 54 | 55 | describe('count', function () { 56 | it('should be a function', function () { 57 | expect(children.count).to.be.a('function'); 58 | }); 59 | 60 | it('should return length for children', function () { 61 | expect(children.count(oneInArray)).to.equal(oneInArray.length); 62 | expect(children.count(twoInArray)).to.equal(twoInArray.length); 63 | }); 64 | 65 | it('should return 1 for child', function () { 66 | expect(children.count(child)).to.equal(1); 67 | }); 68 | 69 | it('should return 0 for null/undefined', function () { 70 | expect(children.count(null)).to.equal(0); 71 | expect(children.count(undefined)).to.equal(0); 72 | }); 73 | }); 74 | 75 | describe('only', function () { 76 | it('should be a function', function () { 77 | expect(children.only).to.be.a('function'); 78 | }); 79 | 80 | it('should return null for children', function () { 81 | expect(children.only(oneInArray)).to.equal(null); 82 | expect(children.only(twoInArray)).to.equal(null); 83 | }); 84 | 85 | it('should return itself for any types except array', function () { 86 | expect(children.only(null)).to.equal(null); 87 | expect(children.only(undefined)).to.equal(undefined); 88 | expect(children.only(child)).to.equal(child); 89 | }); 90 | }); 91 | 92 | describe('map', function () { 93 | var mapper = function (child, index) { 94 | return index; 95 | }; 96 | 97 | it('should be a function', function () { 98 | expect(children.map).to.be.a('function'); 99 | }); 100 | 101 | it('should return null for null', function () { 102 | expect(children.map(null)).to.equal(null); 103 | }); 104 | 105 | it('should return undefined for undefined', function () { 106 | expect(children.map(null)).to.equal(null); 107 | }); 108 | 109 | it('should map child', function () { 110 | expect(children.map(child, mapper)[0]).to.equal(0); 111 | }); 112 | 113 | it('should map children', function () { 114 | expect(children.map(oneInArray, mapper)).to.deep.equal([0]); 115 | expect(children.map(twoInArray, mapper)).to.deep.equal([0, 1]); 116 | }); 117 | 118 | it('should apply specific context', function () { 119 | expect(children.map(oneInArray, function (child, index) { 120 | return this.prefix + '-' + index; 121 | }, {prefix: 'prefix'})).to.deep.equal(['prefix-0']); 122 | }); 123 | }); 124 | 125 | describe('forEach', function () { 126 | it('should be a function', function () { 127 | expect(children.forEach).to.be.a('function'); 128 | }); 129 | 130 | it('should return null for null', function () { 131 | expect(children.forEach(null)).to.equal(null); 132 | }); 133 | 134 | it('should return undefined for undefined', function () { 135 | expect(children.forEach(null)).to.equal(null); 136 | }); 137 | 138 | it('should forEach child', function () { 139 | var result = []; 140 | children.forEach(child, function (child, index) { 141 | result.push(index); 142 | }); 143 | 144 | expect(result).to.deep.equal([0]); 145 | }); 146 | 147 | it('should iterate from first to last', function () { 148 | var result = []; 149 | children.forEach(twoInArray, function (child, index) { 150 | result.push(child); 151 | }); 152 | 153 | expect(result).to.deep.equal(twoInArray); 154 | }); 155 | 156 | it('should map children', function () { 157 | var result = []; 158 | children.forEach(twoInArray, function (child, index) { 159 | result.push(index); 160 | }); 161 | 162 | expect(result).to.deep.equal([0, 1]); 163 | }); 164 | 165 | it('should apply specific context', function () { 166 | var result = []; 167 | children.forEach(oneInArray, function (child, index) { 168 | result.push(this.prefix + '-' + index); 169 | }, {prefix: 'prefix'}); 170 | 171 | expect(result).to.deep.equal(['prefix-0']); 172 | }); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /test/utils/extend.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | 4 | var extend = require('../../src/utils/extend'); 5 | 6 | var str = 'me a test'; 7 | var integer = 10; 8 | var arr = [1, 'what', new Date(81, 8, 4)]; 9 | var date = new Date(81, 4, 13); 10 | 11 | var Foo = function () {}; 12 | 13 | var obj = { 14 | str: str, 15 | integer: integer, 16 | arr: arr, 17 | date: date, 18 | constructor: 'fake', 19 | isPrototypeOf: 'not a function', 20 | foo: new Foo() 21 | }; 22 | 23 | describe('extend', function () { 24 | it('should be a function', function () { 25 | expect(extend).to.be.a('function'); 26 | }); 27 | 28 | it('without arguments should return an object', function () { 29 | expect(extend()).to.deep.equal({}); 30 | }); 31 | 32 | it('should merge object with object', function () { 33 | var ori = { 34 | str: 'no shit', 35 | integer: 76, 36 | arr: [1, 2, 3, 4], 37 | date: new Date(81, 7, 26), 38 | foo: 'bar' 39 | }; 40 | var target = extend(ori, obj); 41 | var expectedObj = { 42 | str: 'me a test', 43 | integer: 10, 44 | arr: [1, 'what', new Date(81, 8, 4)], 45 | date: new Date(81, 4, 13), 46 | constructor: 'fake', 47 | isPrototypeOf: 'not a function', 48 | foo: new Foo() 49 | }; 50 | var expectedTarget = { 51 | str: 'me a test', 52 | integer: 10, 53 | arr: [1, 'what', new Date(81, 8, 4)], 54 | date: new Date(81, 4, 13), 55 | constructor: 'fake', 56 | isPrototypeOf: 'not a function', 57 | foo: new Foo() 58 | }; 59 | expect(obj).to.deep.equal(expectedObj); 60 | expect(target).to.deep.equal(expectedTarget); 61 | }); 62 | 63 | it('should handle null object normally', function () { 64 | var target = extend(null, obj); 65 | expect(target).to.deep.equal(obj); 66 | }); 67 | 68 | it('should merge any count of objects', function () { 69 | var ori = { 70 | str: 'no shit', 71 | integer: 76, 72 | arr: [1, 2, 3, 4], 73 | date: new Date(81, 7, 26), 74 | foo: 'bar' 75 | }; 76 | var target = extend(ori, obj, {str: 'normal', integer: 0}, {integer: 42}); 77 | var expectedTarget = { 78 | str: 'normal', 79 | integer: 42, 80 | arr: [1, 'what', new Date(81, 8, 4)], 81 | date: new Date(81, 4, 13), 82 | constructor: 'fake', 83 | isPrototypeOf: 'not a function', 84 | foo: new Foo() 85 | }; 86 | expect(target).to.deep.equal(expectedTarget); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/utils/isObject.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | 4 | var isObject = require('../../src/utils/isObject'); 5 | 6 | describe('isObject', function () { 7 | 8 | it('should be a function', function () { 9 | expect(isObject).to.be.a('function'); 10 | }); 11 | 12 | it('without arguments should return false', function () { 13 | expect(isObject()).to.equal(false); 14 | }); 15 | 16 | it('should return true on simple object', function () { 17 | expect(isObject({})).to.equal(true); 18 | }); 19 | 20 | it('should return false on null', function () { 21 | expect(isObject(null)).to.equal(false); 22 | }); 23 | 24 | it('should return false with array', function () { 25 | expect(isObject([])).to.equal(false); 26 | }); 27 | 28 | it('should return false with primitives', function () { 29 | expect(isObject(1)).to.equal(false); 30 | expect(isObject(true)).to.equal(false); 31 | expect(isObject('string')).to.equal(false); 32 | expect(isObject('')).to.equal(false); 33 | }); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /tools/benchmark.js: -------------------------------------------------------------------------------- 1 | var run = require('fast-react-benchmark/run'); 2 | var tests = require('fast-react-benchmark/tests'); 3 | 4 | var ReactRender = require('fast-react-render'); 5 | var ReactServer = require('../src/index'); 6 | 7 | run({ 8 | 'FastReactServer + FastReactRender': tests['FastReactServer + FastReactRender'], 9 | 10 | 'This + FastReactRender': { 11 | engine: ReactServer, 12 | run: function (listView, dataSet) { 13 | var element = ReactServer.createElement(listView, dataSet); 14 | return ReactRender.elementToString(element); 15 | } 16 | } 17 | }, process.env.CHILDREN_COUNT || 100); 18 | --------------------------------------------------------------------------------