├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example.js ├── example ├── hello.jsx ├── index.js ├── index.jsx └── package.json ├── fixtures ├── advanced.jsx ├── component.jsx ├── hello.jsx ├── react.jsx └── this.jsx ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | coverage 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "0.10" 5 | - "0.8" 6 | - "iojs" 7 | before_install: 8 | - "npm install -g npm@2.1.18" 9 | script: 10 | - "npm run test-travis" 11 | after_script: 12 | - "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls" 13 | matrix: 14 | fast_finish: true 15 | allow_failures: 16 | - node_js: "0.8" 17 | - node_js: "iojs" 18 | notifications: 19 | irc: 20 | channels: 21 | - "irc.freenode.org#bigpipe" 22 | on_success: change 23 | on_failure: change 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Arnout Kazemier, Martijn Swaagman, the Contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-jsx 2 | 3 | [![Version npm][version]](http://browsenpm.org/package/react-jsx)[![Build Status][build]](https://travis-ci.org/bigpipe/react-jsx)[![Dependencies][david]](https://david-dm.org/bigpipe/react-jsx)[![Coverage Status][cover]](https://coveralls.io/r/bigpipe/react-jsx?branch=master) 4 | 5 | [from]: https://img.shields.io/badge/from-bigpipe.io-9d8dff.svg?style=flat-square 6 | [version]: http://img.shields.io/npm/v/react-jsx.svg?style=flat-square 7 | [build]: http://img.shields.io/travis/bigpipe/react-jsx/master.svg?style=flat-square 8 | [david]: https://img.shields.io/david/bigpipe/react-jsx.svg?style=flat-square 9 | [cover]: http://img.shields.io/coveralls/bigpipe/react-jsx/master.svg?style=flat-square 10 | 11 | The `react-jsx` module allows you to compile your JSX (`.jsx`) templates to: 12 | 13 | - React's `React.createElement` DOM syntax (default for server and client). 14 | - React's HTML output. 15 | - Pure HTML output. 16 | 17 | These templates can be used on the server **and** client. This way you can move 18 | your `JSX` templates out of your `React.createClass`'s `render` method and to 19 | it's own template files which leads to a more manageable code base. 20 | 21 | In addition to the features mentioned above we also eliminated the requirement 22 | of "global" and "locally" scoped variables in your template. You can now pass in 23 | the data using a `data` argument. 24 | 25 | By using the same templates on the front and back-end you can create 26 | progressively enhanced and SEO friendly web pages. 27 | 28 | ## Table of Contents 29 | 30 | - [Installation](#installation) 31 | - [Versioning](#versioning) 32 | - [Usage](#usage) 33 | - [Passing data around](#passing-data-around) 34 | - [Templates](#templates) 35 | - [Client-side](#client-side) 36 | - [Output](#output) 37 | - [Warnings](#warnings) 38 | - [License](#license) 39 | 40 | ## Installation 41 | 42 | The module is published in the public npm registry and can be installed using: 43 | 44 | ``` 45 | npm install --save react-jsx 46 | ``` 47 | 48 | And that's it! To learn more about how the API works, continue to the [usage] 49 | section. 50 | 51 | ## Versioning 52 | 53 | The minor version of this module is in sync with the version of `react` and 54 | `react-tools` that we depend upon. Bug fixes to this library will be done as 55 | patch releases so you should read our versioning as: 56 | 57 | ``` 58 | .. 59 | ``` 60 | 61 | The earliest version of react that we support is **0.12**. So please note that 62 | our 0.0.x releases CAN include a breaking change so when you're adding this 63 | module to your package.json make sure you put down the full semver version. 64 | 65 | ## Usage 66 | 67 | In all of the examples we assume that you've already required the `jsx` compiler 68 | as following: 69 | 70 | ```js 71 | 'use strict'; 72 | 73 | var jsx = require('react-jsx'); 74 | ``` 75 | 76 | This `jsx` variable now contains 3 methods: 77 | 78 | - **server** Use this method if you want transform your `jsx` templates for 79 | server-side usage. We will automatically inject `React` as global in the 80 | templates so it all works as intended. We will return a function which you can 81 | call to render your template. 82 | - **client** Use this method if you want to transform your `jsx` templates for 83 | client-side usage. It assumes that React is already available as global on the 84 | page. We will return a function which you can call to render you template. 85 | - **transform** Our internal compiler which transforms the JSX templates to a 86 | template API. 87 | 88 | Both the **server** and **client** method share the same API for compiling and 89 | rendering: 90 | 91 | ```js 92 | var template = fs.readFileSync('template.jsx', 'utf-8'); 93 | 94 | var server = jsx.server(template, { /* options */}); 95 | var client = jsx.client(template, {}); 96 | 97 | console.log(server({ data: 'for template' })); 98 | ``` 99 | 100 | And they also share the same options: 101 | 102 | - **filename**: File name of the template file we're about to process. This will 103 | be used for debugging purposes in the inlined source map when you've set 104 | `debug` to true. 105 | - **debug**: When set to `true`, we will automatically inline source map. 106 | - **ecma**: Which ECMA version should the template be compiled towards. It 107 | defaults to `es3` for the client and `es5` on the server. 108 | - **types** Don't use strict types. 109 | - **raw** This allows you to control how the generated HTML is outputted. By 110 | default we output the React generated HTML which is full of `data-react-xxx` 111 | attributes. Setting this option to `true` will return a clean HTML instead. 112 | 113 | When rendering the templates both the server and client method will return the 114 | expected `React.createElement` nodes just like you would normally do in your 115 | templates so you can easily share templates with child/parent relations. If you 116 | want the template methods. But your might want to output the raw/pure HTML 117 | instead. This can be done by supplying `{ html: true }` as option to the 118 | template function: 119 | 120 | ```js 121 | var template = fs.readFileSync('/path/to/template.jsx', 'utf-8') 122 | , render = jsx.server(template, { filename: 'template.jsx' }); 123 | 124 | console.log(render({ foo: 'bar' }, { html: true })); 125 | ``` 126 | 127 | ### Passing data around 128 | 129 | The generated client and server functions accept data or "scope" for the 130 | templates as first argument: 131 | 132 | ```js 133 | render({ foo: 'bar' }); 134 | ``` 135 | 136 | If you want to set a custom `this` context for the template you could call the 137 | returned template function as followed: 138 | 139 | ```js 140 | render.call({ custom: 'this', value: 'is possible' }); 141 | ``` 142 | 143 | But the template function we've generated is smart enough to figure out if 144 | you're passing around React instances and will automatically set the supplied 145 | `data` argument as context: 146 | 147 | ```js 148 | var HelloWorld = React.createClass({ 149 | render: function render() { 150 | return render(this); 151 | } 152 | }); 153 | ``` 154 | 155 | So in the example above the data argument is set to `this` so it will 156 | automatically be introduced as `this` in the template AND all properties and 157 | methods will also be introduced as local variables. So if where to mixins the 158 | `React.Intl` module in the class above your template would have access to 159 | `` components: 160 | 161 | ```js 162 | var HelloWorld = React.createClass({ 163 | mixins: [require('react-intl').IntlMixin] 164 | render: function render() { 165 | return render(this); 166 | } 167 | }); 168 | ``` 169 | 170 | And the template that you would render could then contain: 171 | 172 | ```jsx 173 | } 177 | /> 178 | ``` 179 | 180 | ### Templates 181 | 182 | The `.jsx` templates that you're creating should only contain the parts that are 183 | transformed in to `React.createElement`'s. In addition to that there is no need 184 | to `return` or `module.exports` the template. This is all taken care of under 185 | the hood. The following example would a valid example of this: 186 | 187 | ```jsx 188 |
189 | 190 |
; 191 | ``` 192 | 193 | Working with components isn't a problem either, you can still pass them around 194 | using the `data` argument of the template function as illustrated in this HTTP 195 | server example: 196 | 197 | ```js 198 | var http = require('http') 199 | , path = require('path') 200 | , React = require('react') 201 | , jsx = require('react-jsx') 202 | , read = require('fs').readFileSync; 203 | 204 | var templates = { 205 | hello: jsx.server(read(path.join(__dirname, 'hello.jsx'), 'utf-8')), 206 | index: jsx.server(read(path.join(__dirname, 'index.jsx'), 'utf-8')) 207 | }; 208 | 209 | var HelloWorld = React.createClass({ 210 | render: function render() { 211 | return templates.hello(this); 212 | } 213 | }); 214 | 215 | http.createServer(function (req, res) { 216 | res.statusCode = 200; 217 | res.setHeader('Content-Type', 'text/html'); 218 | 219 | res.end(templates.index({ 220 | HelloWorld: HelloWorld, 221 | title: 'Hello world', 222 | another: 'variable' 223 | }, { html: true })); 224 | }).listen(8080); 225 | ``` 226 | 227 | ```jsx 228 | /* index.jsx */ 229 | 230 | 231 | {title} 232 | 233 | 234 | 235 | 236 | 237 | ``` 238 | 239 | ```jsx 240 | /* hello.jsx */ 241 |
242 | Hello world, you're just another {this.props.name} 243 |
244 | ``` 245 | 246 | ### Client-side 247 | 248 | The client that we generate is a function but is optimized for ES3 so it works 249 | in older browser versions without any addition hassle. As the `jsx.client()` 250 | method returns a function you might need to transform this to a string if you 251 | want to use it for client side templates. The string transformation is quite 252 | easy to do: 253 | 254 | ```js 255 | var build = 'var mytemplate = '+ jsx.client(template).toString(); 256 | ``` 257 | 258 | The `.toString()` method automatically transforms the function in to an 259 | anonymous function. In the example above we saved the template as `mytemplate` 260 | variable. So when we store this a JavaScript file to disk using a 261 | `fs.writeFileSync('/mytemplate.js', build);` we can easily access the 262 | template on the client side by referencing the `mytemplate` global. 263 | 264 | So with this knowledge, an illustration of this: 265 | 266 | ```js 267 | var Component = React.createClass({ 268 | render: function render() { 269 | return mytemplate({ foo: 'bar' }); 270 | } 271 | }); 272 | ``` 273 | 274 | ### Output 275 | 276 | To give you an idea about what we're actually generating here, lets take the 277 | following JSX template and convert it a client and server template: 278 | 279 | ```jsx 280 |
281 | 282 | 283 |
    284 | {['un', 'deux', 'trois'].map(function(number) { 285 | return
  • {number}
  • ; 286 | })} 287 |
288 |
; 289 | ``` 290 | 291 | When we compile this template for server-side usage with the **raw** and 292 | **html** options enabled: 293 | 294 | ```js 295 | var server = jsx.server(template, { raw: true }); 296 | console.log(server({ defaultValue: 10 }, { html: true })); 297 | ``` 298 | 299 | It will generate the following output: 300 | 301 | ```html 302 |
  • un
  • deux
  • trois
303 | ``` 304 | 305 | And with the **raw** option set to **false** it will generate: 306 | 307 | ```html 308 |
  • un
  • deux
  • trois
309 | ``` 310 | 311 | But by default we will just return React.createElement structures: 312 | 313 | ```js 314 | var client = jsx.client(template); 315 | console.log(client({ defaultValue: 10 })); 316 | ``` 317 | 318 | Returns the expected `React.createElement` structure: 319 | 320 | ```js 321 | React.createElement("div", null, 322 | React.createElement("input", {type: "text", value: defaultValue}), 323 | React.createElement("button", {onclick: "alert('clicked!');"}, "Click Me!"), 324 | React.createElement("ul", null, 325 | ['un', 'deux', 'trois'].map(function(number) { 326 | return React.createElement("li", null, number); 327 | }) 328 | ) 329 | ); 330 | ``` 331 | 332 | ## Warnings 333 | 334 | As we are using the `react-tools` to compile the templates to all the nice 335 | things it can happen that it output's "useful" information about your templates 336 | in the terminal. For example for the template used above you would see the 337 | following warning in your terminal: 338 | 339 | ``` 340 | Warning: You provided a `value` prop to a form field without an `onChange` 341 | handler. This will render a read-only field. If the field should be mutable use 342 | `defaultValue`. Otherwise, set either `onChange` or `readOnly`. 343 | ``` 344 | 345 | There's not really a way to prevent this from happening except for running your 346 | code with `NODE_ENV=production` as this will silence the warnings. 347 | 348 | ## License 349 | 350 | MIT 351 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path') 4 | , jsx = require('./') 5 | , fs = require('fs'); 6 | 7 | var template = fs.readFileSync(path.join(__dirname, 'fixtures', 'advanced.jsx'), 'utf-8') 8 | , raw = jsx.server(template, { raw: true }) 9 | , server = jsx.server(template) 10 | , client = jsx.client(template) 11 | , data = { defaultValue: 10 }; 12 | 13 | console.log(raw(data)); 14 | console.log(server(data)); 15 | console.log(client.toString(data)); 16 | -------------------------------------------------------------------------------- /example/hello.jsx: -------------------------------------------------------------------------------- 1 | /* hello.jsx */ 2 |
3 | Hello world, you're just another {this.props.name} 4 |
5 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var jsx = require('../') 4 | , http = require('http') 5 | , path = require('path') 6 | , React = require('react') 7 | , read = require('fs').readFileSync; 8 | 9 | var templates = { 10 | hello: jsx.server(read(path.join(__dirname, 'hello.jsx'), 'utf-8')), 11 | index: jsx.server(read(path.join(__dirname, 'index.jsx'), 'utf-8')) 12 | }; 13 | 14 | var HelloWorld = React.createClass({ 15 | render: function render() { 16 | return templates.hello(this); 17 | } 18 | }); 19 | 20 | http.createServer(function (req, res) { 21 | res.statusCode = 200; 22 | res.setHeader('Content-Type', 'text/html'); 23 | 24 | res.end(templates.index({ 25 | HelloWorld: HelloWorld, 26 | title: 'Hello world', 27 | another: 'variable' 28 | }, { html: true })); 29 | }).listen(8080); 30 | -------------------------------------------------------------------------------- /example/index.jsx: -------------------------------------------------------------------------------- 1 | /* index.jsx */ 2 | 3 | 4 | {title} 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "Template rendering example", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/bigpipe/react-jsx" 12 | }, 13 | "author": "Arnout Kazemier", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/bigpipe/react-jsx/issues" 17 | }, 18 | "homepage": "https://github.com/bigpipe/react-jsx", 19 | "dependencies": { 20 | "react": "0.14.x" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fixtures/advanced.jsx: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
    5 | {['un', 'deux', 'trois'].map(function(number) { 6 | return
  • {number}
  • ; 7 | })} 8 |
9 |
; 10 | -------------------------------------------------------------------------------- /fixtures/component.jsx: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fixtures/hello.jsx: -------------------------------------------------------------------------------- 1 |
Hello {props.name}
2 | -------------------------------------------------------------------------------- /fixtures/react.jsx: -------------------------------------------------------------------------------- 1 |
content
; 2 | -------------------------------------------------------------------------------- /fixtures/this.jsx: -------------------------------------------------------------------------------- 1 |
{this.props.name}
2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var compiler = require('babel-standalone') 4 | , React = require('react') 5 | , ReactDOM = require('react-dom/server'); 6 | /** 7 | * Watch out, here be demons. This is a hack to prevent JSX from assuming 8 | * globals everywhere. It's less harmful on the client as those are only used 9 | * for one client/user but when you are serving templates on the server to 10 | * hundreds of concurrent users you don't really want to start sharing global 11 | * variables. So what we do is force a `with` call in the template which 12 | * introduces all the keys of the supplied `data` argument as local variables. 13 | * 14 | * To make sure that we the generated React.createElement code we need to insert 15 | * a `return` statement in the function body. The safest way of doing this was 16 | * by searching for the first occurrence of React.createElement and insert 17 | * a `return` statement before it. This however limits the use to only "root" 18 | * element per template. 19 | * 20 | * @param {String} tpl Template contents. 21 | * @param {Object} config Configuration for the React JSX compiler. 22 | * @param {Object} options Template configuration. 23 | * @api private 24 | */ 25 | function transform(tpl, config, options) { 26 | var transformed = compiler.transform(tpl, config); 27 | var rdom = transformed.code; 28 | var start = rdom.indexOf('React.createElement'); 29 | 30 | return new Function('data', 'config', [ 31 | 'data = data || {};', 32 | 33 | 'var nodes = (function jsx() {', 34 | rdom.slice(0, start), 35 | 'with (data) return '+ rdom.slice(start), 36 | '}).call(this.props ? this : data),', 37 | 'options = '+ JSON.stringify(options || {}) +';', 38 | 39 | 'if ("DOM" === options.render || !(config || {}).html) return nodes;', 40 | 'return ReactDOM[options.render](nodes);' 41 | ].join('\n')); 42 | } 43 | 44 | /** 45 | * Compile the JSX template from client-side usage. 46 | * 47 | * @param {String} tpl JSX template string. 48 | * @param {Object} options Compilation configuration. 49 | * @returns {Function} 50 | * @api public 51 | */ 52 | function client(tpl, options) { 53 | options = options || {}; 54 | 55 | /** 56 | * The template render method which returns React DOM elements. 57 | * 58 | * @param {Object} data Template variables that should be introduced. 59 | * @returns {React} 60 | * @api public 61 | */ 62 | return transform(tpl, { 63 | filename: options.filename, 64 | sourceMaps: !!options.debug, 65 | presets: ['react'], 66 | }, { 67 | render: options.render || (options.raw ? 'renderToStaticMarkup' : 'renderToString') 68 | }); 69 | } 70 | 71 | /** 72 | * Compile the JSX template from client-side usage. 73 | * 74 | * @param {String} tpl JSX template string. 75 | * @param {Object} options Compilation configuration. 76 | * @returns {Function} 77 | * @api public 78 | */ 79 | function server(tpl, options) { 80 | options = options || {}; 81 | 82 | /** 83 | * The template render method which uses the compiled React-template to do all 84 | * the things. 85 | * 86 | * @param {Object} data Template variables that should be introduced. 87 | * @param {Object} config Override configuration. 88 | * @returns {String} 89 | * @api public 90 | */ 91 | return (new Function('React', 'ReactDOM', 'return '+ transform(tpl, { 92 | filename: options.filename, 93 | sourceMaps: !!options.debug, 94 | presets: ['react'], 95 | }, { 96 | render: options.render || (options.raw ? 'renderToStaticMarkup' : 'renderToString') 97 | })))(React, ReactDOM); 98 | } 99 | 100 | // 101 | // Expose all the various API's 102 | // 103 | exports.transform = transform; 104 | exports.server = server; 105 | exports.client = client; 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx", 3 | "version": "1.0.0", 4 | "description": "Compile JSX templates to client and server-side renderable templates", 5 | "main": "index.js", 6 | "scripts": { 7 | "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", 8 | "test": "mocha test.js", 9 | "watch": "mocha --watch test.js", 10 | "coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js", 11 | "test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/bigpipe/react-jsx" 16 | }, 17 | "keywords": [ 18 | "react", 19 | "jsx", 20 | "compile", 21 | "all", 22 | "the", 23 | "things" 24 | ], 25 | "author": "Arnout Kazemier", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/bigpipe/react-jsx/issues" 29 | }, 30 | "homepage": "https://github.com/bigpipe/react-jsx", 31 | "dependencies": { 32 | "babel-preset-react": "^6.5.0", 33 | "babel-standalone": "^6.4.4", 34 | "react": "~15.4.0", 35 | "react-dom": "^15.4.0" 36 | }, 37 | "devDependencies": { 38 | "assume": "~1.5.0", 39 | "istanbul": "~0.4.5", 40 | "mocha": "~3.1.2", 41 | "pre-commit": "~1.2.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | describe('react-jsx', function () { 2 | 'use strict'; 3 | 4 | // 5 | // Turn off the annoying jsx warnings by forcing production. 6 | // 7 | process.env.NODE_ENV = 'production'; 8 | 9 | var assume = require('assume') 10 | , React = require('react') 11 | , ReactDOM = require('react-dom/server') 12 | , path = require('path') 13 | , jsx = require('./') 14 | , fs = require('fs') 15 | , fixtures 16 | , from; 17 | 18 | // 19 | // Read all the templates from the fixtures directly to make sure that we can 20 | // render all the things correctly. 21 | // 22 | from = path.join(__dirname, 'fixtures'); 23 | fixtures = fs.readdirSync(from).reduce(function each(fixtures, file) { 24 | if (path.extname(file) !== '.jsx') return fixtures; 25 | 26 | fixtures[file.slice(0, -4)] = fs.readFileSync(path.join(from, file), 'UTF-8'); 27 | 28 | return fixtures; 29 | }, {}); 30 | 31 | var Hello = React.createClass({ 32 | render: function render() { 33 | return jsx.server(fixtures.hello)(this); 34 | } 35 | }); 36 | 37 | describe('.client', function () { 38 | before(function () { 39 | global.React = require('react'); 40 | global.ReactDOM = require('react-dom/server'); 41 | }); 42 | 43 | it('is exported as a function', function () { 44 | assume(jsx.client).is.a('function'); 45 | }); 46 | 47 | it('can compile the `react.jsx` template', function () { 48 | var client = jsx.client(fixtures.react); 49 | 50 | assume(client).is.a('function'); 51 | }); 52 | 53 | it('can render the `react.jsx` to a DOM node', function () { 54 | var client = jsx.client(fixtures.react); 55 | 56 | assume(client).is.a('function'); 57 | assume(React.isValidElement(client())).is.true(); 58 | }); 59 | 60 | it('can introduce local variables', function () { 61 | var client = jsx.client(fixtures.advanced); 62 | 63 | assume(client).is.a('function'); 64 | assume(React.isValidElement(client({ defaultValue: 1 }))).is.true(); 65 | }); 66 | 67 | it('can also output HTML', function () { 68 | var client = jsx.client(fixtures.react, { raw: true }); 69 | 70 | assume(client({}, { html: true })).equals('
content
'); 71 | }); 72 | }); 73 | 74 | describe('.server', function () { 75 | before(function () { 76 | delete global.React; 77 | delete global.ReactDOM; 78 | }); 79 | 80 | it('is exported as a function', function () { 81 | assume(jsx.server).is.a('function'); 82 | }); 83 | 84 | it('returns React.createElement by default', function () { 85 | var server = jsx.server(fixtures.react); 86 | 87 | assume(React.isValidElement(server())).is.true(); 88 | }); 89 | 90 | it('can compile the `react.jsx` template', function () { 91 | var server = jsx.server(fixtures.react); 92 | 93 | assume(server).is.a('function'); 94 | }); 95 | 96 | it('can render the `react.jsx` to a raw template string', function () { 97 | var server = jsx.server(fixtures.react, { raw: true }); 98 | 99 | assume(server).is.a('function'); 100 | assume(server({}, { html: true })).equals('
content
'); 101 | }); 102 | 103 | it('can render the `react.jsx` to a react template string', function () { 104 | var server = jsx.server(fixtures.react); 105 | 106 | assume(server).is.a('function'); 107 | assume(server({}, { html: true })).includes('data-reactid'); 108 | assume(server({}, { html: true })).includes('data-react-checksum'); 109 | assume(server({}, { html: true })).includes('content'); 110 | assume(server({}, { html: true })).includes('div'); 111 | }); 112 | 113 | it('can introduce local variables', function () { 114 | var server = jsx.server(fixtures.advanced); 115 | 116 | assume(server).is.a('function'); 117 | assume(server({ defaultValue: 1 }, { html: true })).includes('button'); 118 | }); 119 | 120 | it('can render components', function () { 121 | var server = jsx.server(fixtures.component, { raw: true }) 122 | , result = server({ 123 | Hello: Hello, 124 | namethings: function name(named) { 125 | return named; 126 | } 127 | }, { html: true }); 128 | 129 | assume(result).equals('
Hello john
'); 130 | }); 131 | 132 | it('assumes data should be used as this if it has props', function () { 133 | var server = jsx.server('', { raw: true }) 134 | , that = jsx.server(fixtures.this); 135 | 136 | var Hello = React.createClass({ 137 | render: function () { 138 | return that(this); 139 | } 140 | }); 141 | 142 | assume(server).is.a('function'); 143 | assume(server({ Hello: Hello }, { html: true })).equals('
lol
'); 144 | }); 145 | 146 | it('works with a custom `this`', function () { 147 | var that = jsx.server(fixtures.this, { raw: true }); 148 | 149 | assume(that.call({ 150 | props: { name: 'john' } 151 | }, {}, { html: true })).equals('
john
'); 152 | }); 153 | }); 154 | }); 155 | --------------------------------------------------------------------------------