UNPKG

7.58 kBJavaScriptView Raw
1'use strict';
2
3var React = require('react');
4
5var _require = require('fs'),
6 readdirSync = _require.readdirSync;
7
8var _require2 = require('path'),
9 resolve = _require2.resolve;
10
11var _require3 = require('react-router-config'),
12 renderRoutes = _require3.renderRoutes,
13 matchRoutes = _require3.matchRoutes;
14
15var _require4 = require('./helpers.js'),
16 isObject = _require4.isObject,
17 isFunction = _require4.isFunction,
18 isString = _require4.isString,
19 isArray = _require4.isArray,
20 isBoolean = _require4.isBoolean,
21 arrayHasValues = _require4.arrayHasValues,
22 resolveComponent = _require4.resolveComponent,
23 renderComponent = _require4.renderComponent,
24 avoidXSS = _require4.avoidXSS,
25 objectHasValues = _require4.objectHasValues,
26 getComponentByPathname = _require4.getComponentByPathname,
27 getComponentFromRoutes = _require4.getComponentFromRoutes;
28
29module.exports = function (options) {
30 if (isObject(options)) {
31
32 // Prepare the middleware:
33 var middleware = function middleware(req, res, next) {
34
35 function prepareComponent() {
36 if (routes) {
37 var props = { title: 'Untitled' };
38
39 if (arguments[0]) {
40 if (isObject(arguments[0])) {
41 props = Object.assign(props, arguments[0]);
42 }
43 }
44
45 var results = getComponentFromRoutes(options.routes.collection, url ? url : originalUrl ? req.originalUrl : req.url, props, extract);
46
47 results.reactRouter = true;
48
49 return { component: results.Component, props: results };
50 } else {
51 if (arguments[0]) {
52 if (isString(arguments[0])) {
53 /* Require the component: */
54 var component = resolveComponent(resolve(componentsPath, arguments[0]));
55
56 if (component) {
57 var _props = { title: 'Untitled' };
58
59 if (arguments.length === 3 && isFunction(arguments[arguments.length - 1])) {
60 if (arguments[1]) {
61 if (isObject(arguments[1])) {
62 _props = Object.assign(_props, arguments[1]);
63 }
64 }
65 }
66
67 return { component: component, props: { reactRouter: false, props: _props } };
68 } else {
69 throw 'component was not found in the filesystem';
70 }
71 } else {
72 throw 'component argument must be a string type';
73 }
74 } else {
75 throw 'component argument must be defined';
76 }
77 }
78 }
79
80 function prepareContent(_url, component, props, template, id) {
81 // -------------------------------------------------------- Content:
82 var content = renderComponent(_url, component, props);
83 var $ = require('cheerio').load(template);
84 $('title').text(props.props.title);
85 $('head').append('<script id="__initial_state__">window.__INITIAL_STATE__ = ' + avoidXSS(props) + ';</script>');
86 $('#' + id).html(content.html);
87
88 // -------------------------------------------------------- Return:
89 return {
90 html: $.html(),
91 context: content.context,
92 component: {
93 original: component,
94 rendered: content.html
95 },
96 props: {
97 original: props.props,
98 stringify: avoidXSS(props.props)
99 },
100 template: template,
101 changes: {
102 title: $('title').html(),
103 state: $('#__initial_state__').html(),
104 mount: $('#' + id).html()
105 },
106 routes: routes ? options.routes.collection : null,
107 route: routes ? getComponentByPathname(options.routes.collection, _url) : null
108 };
109 }
110
111 function prepareResults(results, callback) {
112 if (isFunction(callback)) {
113 return callback(results);
114 } else {
115 return results;
116 }
117 }
118
119 // Description: Add a new function called "render" to the req object:
120 req.render = function render() {
121 // ---------------------------------------------------------- Component & Props:
122 var _prepareComponent = prepareComponent.apply(undefined, arguments),
123 component = _prepareComponent.component,
124 props = _prepareComponent.props;
125
126 // ---------------------------------------------------------- Content:
127
128
129 var results = prepareContent(url ? url : originalUrl ? req.originalUrl : req.url, component, props, templateHTML, mountId);
130
131 // ---------------------------------------------------------- Return:
132 return prepareResults(results, arguments[arguments.length - 1]);
133 };
134
135 // Call next:
136 return next();
137 };
138
139 // Get variables from options (if they were passed...)
140 var templateHTML = options.templateHTML,
141 mountId = options.mountId,
142 componentsPath = options.componentsPath,
143 originalUrl = options.originalUrl,
144 url = options.url;
145
146 var routes = false;
147 var extract = false;
148
149 // Check if routes option is valid:
150 if ('routes' in options) {
151 if (!isObject(options.routes)) {
152 throw '"routes" property must be an object.';
153 } else {
154 if ('collection' in options.routes) {
155 if (!isArray(options.routes.collection)) {
156 throw '"collection" property must be an array type.';
157 }
158 } else {
159 throw '"routes" property must have a "collection" property.';
160 }
161
162 if ('extractComponent' in options.routes) {
163 if (!isBoolean(options.routes.extractComponent)) {
164 throw '"extractComponent" property must be a boolean type.';
165 } else {
166 extract = options.routes.extractComponent;
167 }
168 } else {
169 extract = false;
170 }
171 }
172
173 // if (!isArray(options.routes)) {
174 // throw new Error('"routes" property must be an array.');
175 // }
176
177 if (arrayHasValues(options.routes.collection)) {
178 routes = true;
179 }
180 }
181
182 // This option is used independently if routes were found or not.
183 if (!templateHTML) {
184 throw '"templateHTML" property must be defined';
185 } else {
186 if (!isString(templateHTML)) {
187 throw '"templateHTML" must be a string path type';
188 }
189 }
190
191 // This option is used independently if routes were found or not.
192 if (!mountId) {
193 throw '"mountId" property must be defined';
194 } else {
195 if (!isString(mountId)) {
196 throw '"mountId" must be a string path type';
197 } else {
198 if (!templateHTML.includes('id="' + mountId + '"')) {
199 throw '"mountId" was not found in the template';
200 }
201 }
202 }
203
204 // Check if componentsPath option is valid (only if routes were not found):
205 if (!routes) {
206 // Prepare component in case routes weren't provided in options.
207 if (!componentsPath) {
208 throw '"componentsPath" property must be defined';
209 } else {
210 if (!isString(componentsPath)) {
211 throw '"componentsPath" must be a string path type';
212 } else {
213 try {
214 readdirSync(componentsPath, { encoding: 'UTF-8' });
215 } catch (err) {
216 throw '\n\nReason: Directory doesn\'t exists in the filesystem.\ncomponentsPath: "' + componentsPath + '"\nCode: "' + err.code + '"\n';
217 }
218 }
219 }
220 };
221
222 // Return the middleware:
223 return middleware;
224 } else {
225 throw 'Options object was not passed to the middleware.';
226 }
227};