├── .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 [](https://travis-ci.org/alt-j/react-server) [](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 |
--------------------------------------------------------------------------------