UNPKG

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