-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindex.js
105 lines (96 loc) · 3.25 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
'use strict';
var compiler = require('babel-standalone')
, React = require('react')
, ReactDOM = require('react-dom/server');
/**
* Watch out, here be demons. This is a hack to prevent JSX from assuming
* globals everywhere. It's less harmful on the client as those are only used
* for one client/user but when you are serving templates on the server to
* hundreds of concurrent users you don't really want to start sharing global
* variables. So what we do is force a `with` call in the template which
* introduces all the keys of the supplied `data` argument as local variables.
*
* To make sure that we the generated React.createElement code we need to insert
* a `return` statement in the function body. The safest way of doing this was
* by searching for the first occurrence of React.createElement and insert
* a `return` statement before it. This however limits the use to only "root"
* element per template.
*
* @param {String} tpl Template contents.
* @param {Object} config Configuration for the React JSX compiler.
* @param {Object} options Template configuration.
* @api private
*/
function transform(tpl, config, options) {
var transformed = compiler.transform(tpl, config);
var rdom = transformed.code;
var start = rdom.indexOf('React.createElement');
return new Function('data', 'config', [
'data = data || {};',
'var nodes = (function jsx() {',
rdom.slice(0, start),
'with (data) return '+ rdom.slice(start),
'}).call(this.props ? this : data),',
'options = '+ JSON.stringify(options || {}) +';',
'if ("DOM" === options.render || !(config || {}).html) return nodes;',
'return ReactDOM[options.render](nodes);'
].join('\n'));
}
/**
* Compile the JSX template from client-side usage.
*
* @param {String} tpl JSX template string.
* @param {Object} options Compilation configuration.
* @returns {Function}
* @api public
*/
function client(tpl, options) {
options = options || {};
/**
* The template render method which returns React DOM elements.
*
* @param {Object} data Template variables that should be introduced.
* @returns {React}
* @api public
*/
return transform(tpl, {
filename: options.filename,
sourceMaps: !!options.debug,
presets: ['react'],
}, {
render: options.render || (options.raw ? 'renderToStaticMarkup' : 'renderToString')
});
}
/**
* Compile the JSX template from client-side usage.
*
* @param {String} tpl JSX template string.
* @param {Object} options Compilation configuration.
* @returns {Function}
* @api public
*/
function server(tpl, options) {
options = options || {};
/**
* The template render method which uses the compiled React-template to do all
* the things.
*
* @param {Object} data Template variables that should be introduced.
* @param {Object} config Override configuration.
* @returns {String}
* @api public
*/
return (new Function('React', 'ReactDOM', 'return '+ transform(tpl, {
filename: options.filename,
sourceMaps: !!options.debug,
presets: ['react'],
}, {
render: options.render || (options.raw ? 'renderToStaticMarkup' : 'renderToString')
})))(React, ReactDOM);
}
//
// Expose all the various API's
//
exports.transform = transform;
exports.server = server;
exports.client = client;